/* ==========================================================================
   games.zzxcd.cn · uno.css
   --------------------------------------------------------------------------
   UNO 对局界面：对手座位、中央牌桌（牌堆 / 弃牌堆 / 方向 / 当前色）、手牌、
   操作区、选色轮、结算板。

   只归属 games/uno/index.html —— 房间大厅那些通用件在 table.css 里。

   ⚠️ 两条世界规则（DESIGN.md）：
      · 牌面四色是**物件**：固定不随主题变（先例：shell.css 的 --ck-felt 棋盘毡面）；
        台面 / 文字 / 描边才随主题走
      · 进度与方向**不做插值动画**：一格一格跳（The Discrete Step Rule）
   ========================================================================== */

:root {
  /* UNO 四色 —— 与 games/assets/cards/uno-deck.svg 的调色板一致（#ff5555/#ffaa00/#55aa55/#5555ff） */
  --uno-red: #ff5555;
  --uno-yellow: #ffaa00;
  --uno-green: #55aa55;
  --uno-blue: #5555ff;
  --uno-card-scale: 1;                                  /* 设置里的「牌面大小」改这个倍率 */
  --uno-card-w: calc(4.6rem * var(--uno-card-scale));
}

/* 设置里的「动画」选精简：出牌/抽牌不做位移，只换内容（结构不变，少一层动效） */
body.uno-calm .uno-discard .uno-card { animation: none; }

/* ---------- 顶栏 ---------- */
.uno-top {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  margin-bottom: var(--sp-2);
}

.uno-room {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  height: 2rem;
  padding: 0 0.6rem;
  border: var(--rule);
  background: var(--sheet);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  white-space: nowrap;
}

.uno-room b { letter-spacing: 0.16em; }

.uno-stat {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  height: 2rem;
  padding: 0 0.6rem;
  border: var(--rule);
  background: var(--sheet);
  font-size: 0.72rem;
  white-space: nowrap;
}

.uno-stat span {
  font-family: var(--font-mono);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* ---------- 牌序轨道：按出手顺序铺开 ---------- */
.uno-rail {
  display: flex;
  align-items: stretch;
  justify-content: center;
  gap: 0.4rem;
  padding: 0.4rem;
  border: var(--rule);
  background: var(--sheet);
  box-shadow: var(--shadow);
  margin-bottom: var(--sp-2);
}

/* 方向钮：箭头翻面 = 出牌方向反了 */
.uno-dirsign {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.15rem;
  flex: 0 0 auto;
  padding: 0.25rem 0.5rem;
  border: 2px solid var(--ink);
  background: var(--paper);
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 700;
  text-align: center;
  white-space: nowrap;
}

.uno-dirsign svg {
  width: 1.4rem;
  height: 1.4rem;
  fill: none;
  stroke: var(--ink);
  stroke-width: 2.4;
  stroke-linecap: square;
  stroke-linejoin: miter;
}

.uno-dirsign.rev svg { transform: scaleX(-1); }

.uno-cells {
  display: flex;
  align-items: stretch;
  justify-content: center;
  /* 边框只包住格子本身（人少时不出现一段空框），人多时最多铺满内容列 */
  width: fit-content;
  max-width: 100%;
  flex: 0 1 auto;
  min-width: 0;
  border: 2px solid var(--ink);
}

/* 一格一个座位；人少时最多 12rem 宽，人多时自动收窄 */
.uno-rail-cell {
  position: relative;
  flex: 0 1 12rem;
  min-width: 5.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding: 0.35rem 0.5rem;
  border-right: 2px solid var(--ink);
  background: var(--sheet);
}

.uno-rail-cell:last-child { border-right: 0; }
.uno-rail-cell.mine { background: color-mix(in srgb, var(--ink) 6%, var(--sheet)); }
.uno-rail-cell.turn { background: var(--ink); color: var(--paper); }
.uno-rail-cell.turn .uno-rail-name,
.uno-rail-cell.turn .uno-rail-count b { color: var(--paper); }
.uno-rail-cell.off { opacity: 0.55; }
.uno-rail-cell.out { opacity: 0.4; }

.uno-rail-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.35rem;
}

.uno-rail-name {
  font-size: 0.78rem;
  font-weight: 800;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.uno-rail-count {
  flex: 0 0 auto;
  display: flex;
  align-items: baseline;
  gap: 0.12rem;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  color: var(--ink-soft);
}

.uno-rail-cell.turn .uno-rail-count { color: var(--paper); }
.uno-rail-count b { font-size: 1rem; font-variant-numeric: tabular-nums; }

.uno-rail-tags {
  display: flex;
  align-items: center;
  gap: 0.22rem;
  flex-wrap: wrap;
}

/* 被跳过：整格闪一下 + 一条斜杠 */
.uno-rail-cell.skipped { animation: uno-skip 1100ms steps(1, end) 1; }

.uno-rail-cell.skipped::after {
  content: '';
  position: absolute;
  left: 0.25rem;
  right: 0.25rem;
  top: 50%;
  height: 3px;
  background: var(--accent);
  transform: rotate(-12deg);
}

@keyframes uno-skip {
  0% { background: var(--accent); color: var(--on-accent); }
  45% { background: var(--accent); color: var(--on-accent); }
  100% { background: var(--sheet); color: var(--ink); }
}

/* 被加牌：整格硬闪两下 */
.uno-rail-cell.hit { animation: uno-hit 520ms steps(1, end) 1; }

@keyframes uno-hit {
  0% { box-shadow: inset 0 0 0 3px var(--accent); }
  50% { box-shadow: inset 0 0 0 3px transparent; }
  100% { box-shadow: inset 0 0 0 3px var(--accent); }
}

/* ↺ 轮完回到谁 */
.uno-loop {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  flex: 0 0 auto;
  padding: 0.2rem 0.45rem;
  border: 2px dashed var(--ink);
  font-family: var(--font-mono);
  font-size: 0.66rem;
  font-weight: 700;
  color: var(--ink-soft);
  white-space: nowrap;
}

.uno-loop b { color: var(--ink); }

/* ---------- 该谁出牌 / 我该做什么 ---------- */
.uno-turn {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.7rem;
  border: var(--rule);
  background: var(--sheet);
  margin-bottom: var(--sp-2);
  font-size: 0.85rem;
  font-weight: 700;
}

.uno-turn-dot {
  width: 0.55rem;
  height: 0.55rem;
  flex: 0 0 auto;
  border: 1px solid var(--ink);
  background: var(--ink-soft);
}

/* ⚠️ 弹性项默认 min-width:auto —— 文案长了不会缩，会**溢出盒子**去贴倒计时。
   给 min-width:0 + 省略号，间隙（gap）才保得住 */
.uno-turn-text {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 倒计时做成离散台阶（世界规则：一格一格跳，不做插值） */
.uno-turn-steps {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 1px;
  flex: 1 1 auto;
  min-width: 3rem;
  max-width: 20rem;
  margin-left: auto;
}

.uno-turn-steps i {
  flex: 0 0 auto;
  width: 0.28rem;
  height: 0.72rem;
  background: color-mix(in srgb, var(--ink) 16%, transparent);
}

.uno-turn-steps i.on { background: var(--ink); }

.uno-turn-sub {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--ink-soft);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.uno-turn[data-state="me"] { background: var(--ink); color: var(--paper); }
.uno-turn[data-state="me"] .uno-turn-dot { background: var(--accent); border-color: var(--accent); }
.uno-turn[data-state="me"] .uno-turn-steps i { background: color-mix(in srgb, var(--paper) 30%, var(--ink)); }
.uno-turn[data-state="me"] .uno-turn-steps i.on { background: var(--accent); }
.uno-turn[data-state="me"] .uno-turn-sub { color: color-mix(in srgb, var(--paper) 74%, var(--ink)); }

.uno-turn[data-state="urgent"] { background: var(--accent); color: var(--on-accent); }
.uno-turn[data-state="urgent"] .uno-turn-dot { background: var(--on-accent); border-color: var(--on-accent); }
.uno-turn[data-state="urgent"] .uno-turn-steps i { background: color-mix(in srgb, var(--on-accent) 34%, var(--accent)); }
.uno-turn[data-state="urgent"] .uno-turn-steps i.on { background: var(--on-accent); }
.uno-turn[data-state="urgent"] .uno-turn-sub { color: color-mix(in srgb, var(--on-accent) 80%, var(--accent)); }

.uno-turn[data-state="over"] { background: var(--paper); }

/* ---------- 台面：牌堆（=抽牌按钮）· 当前色 · 弃牌堆 ---------- */
.uno-table {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border: var(--rule);
  background: var(--sheet);
  box-shadow: var(--shadow);
  margin-bottom: var(--sp-2);
}

/* 牌堆就是抽牌按钮：轮到你且出不了牌时它变成主操作 */
.uno-deck {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  padding: 0.35rem;
  border: 0;
  background: transparent;
  font-family: inherit;
  cursor: default;
}

.uno-deck-stack {
  position: relative;
  display: block;
  width: var(--uno-card-w);
  aspect-ratio: 2 / 3;
}

/* ⚠️ .uno-back 自带 width: var(--uno-card-w) —— 绝对定位下**显式宽度会盖过 inset**，
   所以窄屏把容器缩到 2.8rem 后，牌背仍是 3.4rem 大、往下溢出 15px 把「抽牌」标签压住。
   这里显式钉死 100% 才跟着容器走 */
.uno-deck-stack .uno-back { position: absolute; inset: 0; width: 100%; height: 100%; }
.uno-deck-stack .uno-back:nth-child(1) { transform: translate(-0.22rem, -0.22rem); }
.uno-deck-stack .uno-back:nth-child(2) { transform: translate(-0.11rem, -0.11rem); }

.uno-deck-label {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 700;
  padding: 0 0.4rem;
  border: 2px solid var(--ink);
  background: var(--paper);
  color: var(--ink);
  white-space: nowrap;
}

.uno-deck-num {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

.uno-deck.armed { cursor: pointer; }
.uno-deck.armed .uno-deck-label { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.uno-deck.armed .uno-deck-stack .uno-back:last-child { box-shadow: 3px 3px 0 var(--accent); }
.uno-deck.armed:hover .uno-deck-stack { transform: translateY(-0.2rem); }
.uno-deck:disabled { cursor: default; }

.uno-pile-count {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
}

.uno-mid {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  text-align: center;
}

.uno-mid-note {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  color: var(--ink-soft);
  line-height: 1.5;
  max-width: 16rem;
}

/* 弃牌堆：顶牌 + 一圈当前色环（颜色不能是唯一信号，旁边还有文字） */
.uno-discard {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
}

/* 弃牌堆：顶牌 + 当前色直接做成牌的**边框**
   ⚠️ 原来是 outline-offset:4px 的浮环 → 和牌的墨线边、素材自带的白边凑成三层框，
      看着像外面又套了一层。现在贴边（offset:0）：[当前色环][墨线][牌面]，一圈读完 */
.uno-discard .uno-card {
  --uno-card-w: 5.2rem;
  outline: 5px solid var(--uno-ring, transparent);
  outline-offset: 0;
}

.uno-side-label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--ink-soft);
  letter-spacing: 0.04em;
}

/* 开局那张是「起始牌」，不是谁打出来的 */
.uno-side-hint {
  font-style: normal;
  font-weight: 700;
  padding: 0 0.25rem;
  border: 1px solid var(--ink);
  background: var(--paper);
  color: var(--ink);
}

.uno-side-hint.hidden { display: none; }

/* 当前色 */
.uno-color {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  border: 2px solid var(--ink);
  background: var(--paper);
  padding: 0.25rem 0.5rem;
  font-size: 0.72rem;
  font-weight: 700;
  white-space: nowrap;
}

.uno-sw {
  display: block;
  width: 0.9rem;
  height: 0.9rem;
  border: 1px solid var(--ink);
  flex: 0 0 auto;
}

.uno-sw.r { background: var(--uno-red); }
.uno-sw.y { background: var(--uno-yellow); }
.uno-sw.g { background: var(--uno-green); }
.uno-sw.b { background: var(--uno-blue); }
.uno-sw.none { background: var(--paper); }

.uno-pending {
  border: 2px solid var(--accent);
  background: var(--accent);
  color: var(--on-accent);
  font-size: 0.72rem;
  font-weight: 800;
  padding: 0.2rem 0.5rem;
}

/* 一手打出的牌：从牌堆那一侧滑进来（用独立 translate，不碰 transform） */
.uno-discard.play .uno-card { animation: uno-land 200ms var(--ease) 1; }

@keyframes uno-land {
  from { translate: -2.6rem -0.6rem; }
  to { translate: 0 0; }
}

/* 开局翻起始牌 */
.uno-discard.flip .uno-card { animation: uno-flip-over 260ms var(--ease) 1; }

@keyframes uno-flip-over {
  from { translate: -4.4rem -1.2rem; }
  to { translate: 0 0; }
}

/* 罚抽：N 张牌背从牌堆飞向被罚的人（JS 创建，动画结束即移除） */
.uno-fly {
  position: fixed;
  z-index: 60;
  width: var(--uno-card-w);
  aspect-ratio: 2 / 3;
  border: 2px solid var(--ink);
  background: var(--ink);
  pointer-events: none;
}

.uno-fly::after {
  content: '';
  position: absolute;
  inset: 26%;
  border: 2px solid var(--paper);
  transform: rotate(45deg);
}

/* 受害者大特效：一枚盖在座位上/轨道上的印章（弹出 → 停住 → 收起，离散不插值）
   被加牌盖 +N、被跳过盖「禁止」、反转方向盖在整条轨道上 */
.uno-stamp {
  position: fixed;
  z-index: 70;
  display: grid;
  place-items: center;
  gap: 0.15rem;
  min-width: 6.6rem;
  min-height: 6.6rem;
  padding: 0.5rem 0.8rem;
  border: 4px solid var(--ink);
  background: var(--accent);
  color: var(--on-accent);
  box-shadow: 7px 7px 0 var(--ink);
  pointer-events: none;
  text-align: center;
}

.uno-stamp b {
  font-size: 2.8rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

.uno-stamp span {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.14em;
}

.uno-stamp svg {
  width: 3.6rem;
  height: 3.6rem;
  fill: none;
  stroke: var(--on-accent);
  stroke-width: 2.6;
  stroke-linecap: square;
  stroke-linejoin: miter;
}

/* 墨底版：跳过 / 方向反转 —— 一屏上仍然只有一个强调色实心块 */
.uno-stamp.ink { background: var(--ink); color: var(--paper); box-shadow: 7px 7px 0 var(--accent); }
.uno-stamp.ink svg { stroke: var(--paper); }
.uno-stamp.ink span { color: var(--paper); }

/* 大判罚：被塞牌这种"砸下来"的场合用，比普通印章大一倍 */
.uno-stamp.big {
  min-width: 12rem; min-height: 12rem;
  border-width: 6px;
  box-shadow: 9px 9px 0 var(--ink);
}
.uno-stamp.big b { font-size: 5rem; }
.uno-stamp.big span { font-size: 0.9rem; }

/* 被塞牌：容器震一下（位移写在独立的 translate 上，不动 transform） */
.quake { animation: uno-quake 420ms steps(6, end) 1; }
@keyframes uno-quake {
  0% { translate: 0 0; } 16% { translate: -7px 3px; } 33% { translate: 6px -4px; }
  50% { translate: -5px -3px; } 66% { translate: 4px 4px; } 83% { translate: -3px 1px; }
  100% { translate: 0 0; }
}

/* 刚被塞进手牌的牌：强调色描边 + 抬起来，1.5s 后归位 */
.uno-card.just {
  outline: 4px solid var(--accent);
  outline-offset: 0;
  translate: 0 -0.9rem;
  z-index: 2;
}

/* 其他人视角：那格加粗成强调色边框 + 张数硬跳（不额外塞元素，免得压住座位名） */
.uno-rail-cell.receiving { border-color: var(--accent); box-shadow: inset 0 0 0 3px var(--accent); }
.uno-rail-count.tick { animation: uno-tick 460ms steps(3, end) 1; }
@keyframes uno-tick {
  0% { background: var(--accent); color: var(--on-accent); }
  60% { background: var(--accent); color: var(--on-accent); }
  100% { background: transparent; color: inherit; }
}

/* 房号输入框下面的一行说明：粘贴邀请链接时说清「已经从链接里取到房号 XXXX」 */
.uno-join-hint {
  display: none;
  margin-top: 0.5rem;
  padding: 0.35rem 0.5rem;
  border: 2px solid var(--ink);
  background: var(--paper);
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 1.5;
}

.uno-join-hint.on { display: block; }
.uno-join-hint.ok { background: var(--sheet); }
.uno-join-hint.bad { background: var(--ink); color: var(--paper); }

/* ---------- 牌 ---------- */
.uno-card {
  --uno-col: 0;
  --uno-row: 0;
  width: var(--uno-card-w);
  aspect-ratio: 2 / 3;
  flex: 0 0 auto;
  display: block;
  padding: 0;
  border: 2px solid var(--ink);
  box-shadow: var(--shadow-sm);
  background-image: url('../assets/cards/uno-deck.svg');
  background-repeat: no-repeat;
  /* 素材实测：14 列 × 8 行，单元格 240×360（2:3），首格原点 (1,1) */
  background-size: 1400% 800%;
  background-position: calc(var(--uno-col) * 100% / 13) calc(var(--uno-row) * 100% / 7);
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease), opacity var(--dur) var(--ease);
}

/* 牌背（用户选的方案 A）：**纸背 + 双层墨框 + 中心强调色菱形**
   ⚠️ 原来是「墨底 + 纸色菱形描边」= 一坨死黑；而且暗色主题下 --ink 是近白 →
   会翻成一坨死白，两头都不好看。纸背的写法两个主题都成立（暗色自动变成深纸 + 浅线） */
.uno-back {
  position: relative;            /* ::before/::after 的定位上下文（牌堆里那条 absolute 优先级更高，不受影响） */
  width: var(--uno-card-w);
  aspect-ratio: 2 / 3;
  flex: 0 0 auto;
  border: 2px solid var(--ink);
  background: var(--paper);
  box-shadow: var(--shadow-sm);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* 内框：离边 13%，与卡的外框凑成双层结构线（零圆角世界的写法） */
.uno-back::before {
  content: '';
  position: absolute;
  inset: 13%;
  border: 2px solid var(--ink);
}

/* 中心：强调色实心菱形 —— 跟着主题强调色走，换主题色它跟着变 */
.uno-back::after {
  content: '';
  width: 40%;
  aspect-ratio: 1;
  background: var(--accent);
  border: 2px solid var(--ink);
  transform: rotate(45deg);
}

/* ---------- 我的手牌 ---------- */
.uno-me {
  border: var(--rule);
  background: var(--sheet);
  box-shadow: var(--shadow);
}

.uno-me-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  padding: 0.45rem 0.6rem;
  border-bottom: 3px solid var(--ink);
  font-size: 0.8rem;
  font-weight: 800;
}

.uno-me-head .uno-me-count {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

/* 认输是逃生口，不是主操作：收进标题行，别和「抽牌」抢同一条动作带 */
.uno-quit {
  margin-left: auto;
  padding: 0.1rem 0.45rem;
  border: 2px solid var(--ink);
  background: var(--paper);
  color: var(--ink-soft);
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 700;
}

.uno-quit:hover { border-color: var(--accent); color: var(--accent); }
.uno-quit:active { transform: translate(2px, 2px); }

.uno-hand {
  display: flex;
  align-items: flex-end;
  justify-content: flex-start;   /* 居中靠首尾 auto 边距做，见下：overflow 时左边才滚得到 */
  gap: 0.35rem;
  padding: 1.1rem 0.6rem 1.2rem;
  overflow-x: auto;
  overflow-y: hidden;
  min-height: calc(var(--uno-card-w) * 1.5 + 2rem);
}

/* ⚠️ 不能用 justify-content:center 来居中可滚动的手牌：
   内容溢出时左右两边会被同时裁掉，且左侧永远滚不到（第一张牌点不着）。
   用首尾 auto 边距 —— 放得下就居中，放不下就能从头滚到尾 */
.uno-hand > :first-child { margin-left: auto; }
.uno-hand > :last-child { margin-right: auto; }

/* 相邻叠压：JS 按容器宽度算好压多少（放得下时是 0px，观感与以前一致）
   ⚠️ 只给第 2 张起加负 margin —— first-child 那个 auto 边距要留着做居中 */
.uno-hand .uno-card + .uno-card { margin-left: calc(-1 * var(--uno-hand-overlap, 0px)); }

/* 压到极限还放不下（手牌特别多）才允许滚动：两端渐隐 + 细墨线滚动条，
   别用浏览器那条灰色原生条（窄屏下像一条脏线） */
.uno-hand { scrollbar-width: thin; scrollbar-color: var(--ink) transparent; }
.uno-hand::-webkit-scrollbar { height: 4px; }
.uno-hand::-webkit-scrollbar-track { background: transparent; }
.uno-hand::-webkit-scrollbar-thumb { background: var(--ink); }

.uno-hand.uno-hand-scroll {
  /* 首尾牌旋转后的外接框会外扩 ~30px —— 内边距由 JS 按实测值写进这两个变量 */
  padding-left: var(--uno-hand-pad-l, 1.1rem);
  padding-right: var(--uno-hand-pad-r, 1.1rem);
  mask-image: linear-gradient(to right, transparent 0, #000 1.2rem, #000 calc(100% - 1.2rem), transparent 100%);
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 1.2rem, #000 calc(100% - 1.2rem), transparent 100%);
}

/* 能不能出，静态就要看得出来：
   能出的**抬起来**（位移 + 硬投影），出不去的**沉下去**并且去色 —— 结构信号，不是只靠颜色。
   扇形：每张牌的角度写在 transform 上，抬起写在独立的 translate 上，两者互不干扰 */
.uno-hand .uno-card {
  transition: translate 140ms var(--ease), filter 140ms var(--ease), box-shadow 140ms var(--ease);
  transform-origin: 50% 145%;
}

.uno-hand .uno-card.playable {
  cursor: pointer;
  translate: 0 -0.5rem;
  box-shadow: var(--shadow);
}

.uno-hand .uno-card.playable:hover { translate: 0 -0.8rem; box-shadow: 5px 5px 0 var(--ink); }
.uno-hand .uno-card.playable:active { translate: 2px 2px; box-shadow: 0 0 0 var(--ink); }
.uno-hand .uno-card.playable:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }

.uno-hand .uno-card.dead {
  cursor: not-allowed;
  filter: grayscale(0.55) contrast(0.94);
  opacity: 0.62;
  translate: 0 0.15rem;
}

/* 不是我的回合：平放、保持原色（一局里大部分时间都在等别人，不该一直灰着） */
.uno-hand .uno-card.idle { cursor: default; }

/* 抽到的那张：只有它挂着强调色，提示「只能出这张」 */
.uno-hand .uno-card.drawn { outline: 3px solid var(--accent); outline-offset: 3px; }

/* 发牌：一手牌从牌堆那一侧依次落下 —— 一局里唯一一处「排过」的动效 */
.uno-hand.dealing .uno-card {
  animation: uno-deal 200ms var(--ease) backwards;
  animation-delay: calc(var(--i, 0) * 45ms);
}

@keyframes uno-deal {
  from { translate: -6rem 0.6rem; opacity: 0; }
  to { translate: 0 0; opacity: 1; }
}

.uno-hand-empty {
  padding: 1.2rem 0.6rem;
  font-size: 0.8rem;
  color: var(--ink-soft);
  text-align: center;
}

/* 动作提示：说清现在为什么能点 / 不能点 */
.uno-hint {
  padding: 0 0.7rem 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.uno-actions {
  display: flex;
  gap: var(--sp-2);
  flex-wrap: wrap;
  padding: 0 0.6rem 0.7rem;
  min-height: 0;
}

.uno-actions .tbl-btn { flex: 1 1 6rem; }

/* 只剩一张时的 UNO! —— 这一屏就它一个强调色实心块 */
.uno-call {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
  font-weight: 800;
  letter-spacing: 0.08em;
}

/* 过牌：抽到能出的牌之后才出现，是「我不要」而不是主操作 */
.uno-pass { background: var(--paper); }

/* ---------- +4 质疑裁决 ---------- */
.uno-w4-note {
  margin: 0 0 0.6rem;
  font-size: 0.78rem;
  line-height: 1.6;
  color: var(--ink-soft);
}

.uno-w4-note b { color: var(--ink); }

.uno-w4-odds {
  border: 2px solid var(--ink);
  background: var(--paper);
  padding: 0.45rem 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  font-weight: 700;
  line-height: 1.6;
}

.uno-w4-acts {
  display: flex;
  gap: var(--sp-2);
  margin-top: 0.7rem;
}

.uno-w4-acts .tbl-btn { flex: 1 1 0; }

.uno-w4-timer {
  margin-top: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--ink-soft);
  text-align: center;
}

/* 亮手牌：一排牌，多了换行 */
.uno-reveal-cards {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  justify-content: center;
  padding: 0.2rem 0;
}

.uno-reveal-cards .uno-card { --uno-card-w: 3rem; }

.uno-reveal-note {
  margin-top: 0.7rem;
  border-top: 3px solid var(--ink);
  padding-top: 0.6rem;
  font-size: 0.78rem;
  font-weight: 700;
  line-height: 1.6;
}

/* ---------- 选色轮 ---------- */
.uno-wheel {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--sp-2);
}

.uno-wheel-btn {
  border: 2px solid var(--ink);
  background: var(--sheet);
  box-shadow: var(--shadow-sm);
  padding: 0;
  overflow: hidden;
}

.uno-wheel-btn:active { transform: translate(3px, 3px); box-shadow: 0 0 0 var(--ink); }

/* ⚠️ 色块是 <span>：按钮不是 flex 容器，行内元素的 height 不生效（PITFALLS 第 69 条），
   必须显式 display:block —— 否则选色轮只剩四个文字、色块完全不显示 */
.uno-wheel-sw {
  display: block;
  height: 2.6rem;
  border-bottom: 2px solid var(--ink);
}

.uno-wheel-sw.r { background: var(--uno-red); }
.uno-wheel-sw.y { background: var(--uno-yellow); }
.uno-wheel-sw.g { background: var(--uno-green); }
.uno-wheel-sw.b { background: var(--uno-blue); }

.uno-wheel-label {
  display: block;
  padding: 0.35rem 0;
  font-size: 0.8rem;
  font-weight: 800;
  color: var(--ink);
}

/* ---------- 结算 ---------- */
.uno-result-title {
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  margin-bottom: 0.2rem;
}

.uno-result-sub {
  font-size: 0.8rem;
  color: var(--ink-soft);
  margin-bottom: 0.7rem;
}

.uno-result-lines {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  margin-top: 0.7rem;
  padding-top: 0.6rem;
  border-top: 3px solid var(--ink);
  font-size: 0.76rem;
  color: var(--ink-soft);
}

/* ---------- 大厅 ---------- */
/* 一条竖栈、一个宽度：原来「宽带 → 660 卡片 → 宽带」来回跳，看起来像没做完 */
.uno-stack {
  width: min(100% - 1.6rem, 660px);
  margin-inline: auto;
}

.uno-hero {
  border: var(--rule);
  background: var(--sheet);
  box-shadow: var(--shadow);
  margin-bottom: var(--sp-3);
}

.uno-hero-body {
  padding: 0.7rem;
}

.uno-hero-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2);
  padding: 0.3rem 0;
  font-size: 0.8rem;
  border-bottom: 1px solid color-mix(in srgb, var(--ink) 16%, transparent);
}

.uno-hero-line:last-child { border-bottom: 0; }
.uno-hero-line b { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

.uno-login {
  padding: 0.9rem 0.7rem;
  font-size: 0.8rem;
  line-height: 1.7;
}

.uno-login a { font-weight: 700; }

/* 加入房间：输入框就地展开在这张卡下面 —— 两块合成一块（Seam Rule：用缝分界，不用间距），
   展开时上面那张卡不投影，否则位移块会压在下一块的上边框上（实测压掉 4px） */
.uno-join-wrap { display: grid; gap: 0; }
.uno-join-wrap .card { box-shadow: var(--shadow); }
.uno-join-wrap.open .card {
  box-shadow: none;
  border-bottom: 0;
}
.uno-join-wrap.open .card .chevron { transform: rotate(90deg); }
.uno-join-wrap.open .card:hover { background: var(--sheet); }

.uno-join {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.5rem;
  padding: 0.7rem;
  border: var(--rule);
  border-top: 0;
  background: var(--sheet);
  box-shadow: var(--shadow);
}

/* 展开态：卡片的下边框与这一块的上边框合成一条缝（两块是一块，但结构线要留） */
.uno-join-wrap.open .uno-join { border-top: var(--rule); }

.uno-join .tbl-input { width: 100%; }
.uno-join .tbl-btn { white-space: nowrap; }

/* ---------- 窄屏 ---------- */
@media (max-width: 720px) {
  :root { --uno-card-w: calc(3.4rem * var(--uno-card-scale)); }

  /* 帮助/设置两颗按钮 fixed 在视口右上角（shell.css），窄屏下会压住顶栏 —— 右侧留出空档 */
  .uno-top {
    justify-content: flex-start;
    padding-right: 5.6rem;
    gap: 0.4rem;
  }

  /* 轨道：方向钮横过来、↺ 让位、每格更窄 */
  .uno-rail { gap: 0.3rem; padding: 0.3rem; flex-wrap: wrap; }
  .uno-dirsign { flex-direction: row; gap: 0.3rem; padding: 0.2rem 0.45rem; font-size: 0.64rem; }
  .uno-dirsign svg { width: 1.1rem; height: 1.1rem; }
  .uno-loop { display: none; }
  /* 窄屏按内容排：2–3 人时名字不会被两位数张数挤成「房主 (…」
     （4 人以上放不下时仍按 min-width 收缩 + 省略号，这是可接受的兜底） */
  .uno-rail-cell { padding: 0.3rem 0.35rem; flex: 0 1 auto; }
  .uno-rail-name { font-size: 0.7rem; }
  .uno-rail-count { font-size: 0.62rem; }
  .uno-rail-count b { font-size: 0.86rem; }
  .uno-tag { font-size: 0.62rem; }

  .uno-table { padding: var(--sp-2) 0.5rem; gap: 0.45rem; }
  .uno-discard .uno-card { --uno-card-w: calc(4rem * var(--uno-card-scale)); }
  /* 牌堆原来 2.8rem、弃牌堆 4rem，差 30% 看着像"牌堆被压扁" → 与手牌同档 3.4rem */
  .uno-deck-stack { width: calc(3.4rem * var(--uno-card-scale)); }
  .uno-deck-label,
  .uno-deck-num { font-size: 0.62rem; }
  .uno-mid-note { display: none; }
  .uno-turn { font-size: 0.78rem; padding: 0.45rem 0.55rem; }
  .uno-turn-sub { font-size: 0.68rem; }
  /* 窄屏：倒计时块留出最小宽度，别被文案挤到 0；右尾巴不参与收缩 */
  .uno-turn { gap: 0.45rem; }
  .uno-turn-steps { max-width: 8rem; min-width: 2.4rem; }
  .uno-turn-sub { flex: 0 0 auto; }
  /* 手牌区高度：牌 1.5 倍高 + 上下内距（1.1 + 1.2rem）+ 抬起余量，别让牌上沿被切 */
  .uno-hand { min-height: calc(var(--uno-card-w) * 1.5 + 2.6rem); }
  .uno-actions .tbl-btn { flex: 1 1 4.5rem; padding: 0.5rem 0.4rem; font-size: 0.76rem; }
}

@media (max-width: 400px) {
  .uno-rail-tags .uno-tag.host { display: none; }
  .uno-rail-count span { display: none; }
}
