/* ==========================================================================
   11 · DESKTOP REFINEMENTS — short viewports, QHD, motion, print
   ========================================================================== */

/* short desktop heights: keep the table + console on one screen */
@media (min-width:1001px) and (max-height:940px){
  html{font-size:calc(15.2px * var(--font-scale))}
  .topbar{min-height:56px;padding:7px 14px}
  .game-shell{padding:10px;gap:10px}
  .table-stage{--player-rail-h:clamp(calc(240px * (1 + (var(--ui-widen) - 1) * .32)),40%,calc(262px * (1 + (var(--ui-widen) - 1) * .32)))}
  .board-caption{display:none}
  .table-center-hud{bottom:min(22px, calc(100% - var(--player-rail-h) - 242px));row-gap:5px}
  .community{min-height:80px}
  .community .card{--cw:56px;--ch:79px;--crank:1.3rem;--csuit:1.3rem}
  .hero-card-main{padding:9px 12px}
  .hero-hand .card{--cw:50px;--ch:70px;--crank:1.14rem;--csuit:1.16rem}
  .action-btn{min-height:46px}
  .raise-box{min-height:46px}
  .hero-stat-grid .hero-stat{padding:6px 8px 7px}
}

/* narrow desktop: slimmer sidebar, console keeps its columns */
@media (min-width:1001px) and (max-width:1380px){
  .game-shell{grid-template-columns:minmax(0,1fr) calc(300px * var(--ui-widen))}
  .hero-info{grid-template-columns:minmax(0,1fr) calc(224px * var(--ui-widen))}
  /* base grid demands ≥290px for the raise box — at these widths that pushed the
     raise button past the console's right edge; slim every column instead */
  .controls{width:100%;grid-template-columns:calc(104px * var(--ui-widen)) calc(122px * var(--ui-widen)) minmax(140px,1fr) calc(168px * var(--ui-widen));gap:9px}
  .controls.reveal-return-active{grid-template-columns:calc(104px * var(--ui-widen)) calc(122px * var(--ui-widen)) minmax(0,168px) minmax(0,150px)}
  .expression-picker{padding:10px 11px}
  .expression-hint{display:none}
}
@media (min-width:1001px) and (max-width:1180px){
  .hero-info{grid-template-columns:minmax(0,1fr)}
  .expression-picker{flex-direction:row;align-items:center;gap:10px}
  .expression-current{flex:1}
  .expression-options{flex:1.4}
  .top-stats .stat{min-width:72px}
}

/* tighter desktop widths: shrink + wrap the best-hand mini cards instead of dropping them */
@media (min-width:1001px) and (max-width:1400px){
  .hero-best-hand-cards{flex-wrap:wrap;justify-content:flex-end;max-width:150px}
  .hero-best-hand-cards .card{--cw:26px;--ch:37px;--cr:4px;--crank:.64rem;--csuit:.64rem}
}
/* extremely narrow: the best-hand block stacks under the readout, cards stay visible */
@media (min-width:1001px) and (max-width:1180px){
  .hero-best-hand-cards{max-width:220px}
}

/* QHD / large monitors: scale the whole client up via rem base */
html.desktop-qhd{font-size:calc(19px * var(--font-scale))}
html.desktop-qhd-scaled{font-size:calc(17.5px * var(--font-scale))}
html.desktop-qhd-short{font-size:calc(16.5px * var(--font-scale))}
@media (min-width:2200px){
  .game-shell{grid-template-columns:minmax(0,1fr) calc(380px * var(--ui-widen))}
  .community .card{--cw:74px;--ch:104px;--crank:1.7rem;--csuit:1.7rem}
  /* QHD 根字号 19/16≈1.19 倍，账簿胶囊内容同比例变宽：角色卡列的加宽系数
     从 .25 提到 .58，否则「正在行动 / SB · #8 / 29.6%」在 1.12 字阶即被省略。
     widen=1 时仍为 534px，与 1920 基准一致；1.48 时 682+361+12<1120 上限。 */
  .hero-info{grid-template-columns:minmax(0,calc(534px * (1 + (var(--ui-widen) - 1) * .58))) calc(244px * var(--ui-widen))}
}

/* reduced motion: kill decorative animation, keep state color */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001s!important;animation-iteration-count:1!important;transition-duration:.001s!important;scroll-behavior:auto!important}
}
/* ==========================================================================
   10 · STATE PATCH — JS-toggled state classes (final coverage gap)
   These mirror the old stylesheet's functional rules so no behavior changes.
   ========================================================================== */

/* mode selector close button: only closable once a game exists */
.mode-return-close{display:none}
.mode-return-close.is-available{display:grid}

/* blind progression: dimmed fields while their toggle is off */
.field.is-disabled{opacity:.48}
.field.is-disabled input{cursor:not-allowed}
.blind-setting-card.is-disabled{opacity:.34}
.blind-setting-card.is-disabled:hover{transform:none}
.blind-setting-card.is-disabled input{cursor:not-allowed}
.blind-flow-stage.is-muted{opacity:.35}

/* expression summary: locked vs waiting states */
.expression-current.is-locked{
  border-color:color-mix(in srgb,var(--gold) 42%,transparent);
  background:color-mix(in srgb,var(--gold) 8%,transparent);
}
.expression-current.is-locked .expression-current-state{color:var(--gold)}
.expression-current.is-waiting{opacity:.62}

/* OpenAI connection test result */
.connection-status.ok{
  border-color:color-mix(in srgb,var(--green) 42%,transparent);
  background:color-mix(in srgb,var(--green) 8%,transparent);
  color:#8fe0b8;
}
.connection-status.error{
  border-color:color-mix(in srgb,var(--red) 45%,transparent);
  background:color-mix(in srgb,var(--red) 8%,transparent);
  color:#f2a4ab;
}

/* background library: grid busy state while reading the repository */
.background-library-grid.background-library-loading{pointer-events:none;opacity:.62}

/* account deletion is a destructive action */
.account-delete:hover:not(:disabled){
  border-color:color-mix(in srgb,var(--red) 55%,transparent);
  background:var(--red-soft);
  color:var(--red);
}
/* ==========================================================================
   11 · FINAL COVERAGE — table decor, wager pulse animation, misc JS hooks
   ========================================================================== */

/* ---------- felt environment decorations (per-theme symbols + wordmark) --- */
.environment-decor{position:absolute;inset:0;pointer-events:none;z-index:1;overflow:hidden;border-radius:inherit}
.environment-decor span{
  position:absolute;display:grid;place-items:center;
  color:color-mix(in srgb,var(--accent) 55%,white 5%);
  opacity:.36;filter:drop-shadow(0 4px 12px rgba(0,0,0,.48));
  font-size:22px;
}
.decor-left{left:3.2%;top:50%;transform:translateY(-50%)}
.decor-right{right:3.2%;top:50%;transform:translateY(-50%)}
.decor-top{
  left:50%;top:3.8%;transform:translateX(-50%);
  font-family:var(--font-display);font-size:11px;font-weight:700;letter-spacing:.28em;white-space:nowrap;
}
.decor-left::before{content:"♣"}
.decor-right::before{content:"♦"}
.decor-top::before{content:"地下室 · LOW LIGHT"}
body[data-theme="royal"] .decor-left::before{content:"♛"}
body[data-theme="royal"] .decor-right::before{content:"♜"}
body[data-theme="royal"] .decor-top::before{content:"ROYAL SALON"}
body[data-theme="tavern"] .decor-left::before{content:"🍺"}
body[data-theme="tavern"] .decor-right::before{content:"🎻"}
body[data-theme="tavern"] .decor-top::before{content:"COUNTRY TAVERN"}
body[data-theme="harbor"] .decor-left::before{content:"⚓"}
body[data-theme="harbor"] .decor-right::before{content:"🌊"}
body[data-theme="harbor"] .decor-top::before{content:"HARBOR LOUNGE"}
body[data-theme="carbon"] .decor-left::before{content:"◆"}
body[data-theme="carbon"] .decor-right::before{content:"◇"}
body[data-theme="carbon"] .decor-top::before{content:"PRO CIRCUIT"}

/* ---------- wager total count-up pulse (JS: .wager-changing / .counting) -- */
.wager-total-value,#heroBetLocal{display:inline-block;will-change:opacity,transform,color;transform-origin:center}
.wager-total-value.wager-changing,#heroBetLocal.wager-changing{color:#8ce8ca!important;text-shadow:0 0 10px rgba(103,226,184,.25)}
.counting{animation:wagerTotalPulse .52s ease-out both}
@keyframes wagerTotalPulse{
  0%{transform:translateY(2px) scale(.96);color:#86e9c8}
  65%{transform:translateY(-1px) scale(1.06);color:#d8fff3}
  100%{transform:none;color:#f6d986}
}

/* ---------- reveal/return mode: hero panel must not clip the console ------ */
.hero-panel.reveal-return-mode{overflow:visible}

/* ---------- blind setting card variants ------------------------------------ */
.blind-switch-card{cursor:pointer}
.blind-switch-card:hover{border-color:var(--accent-line);background:var(--accent-soft)}
.blind-value-card{align-items:center}
.setup-blind-preview{margin-top:10px}

/* ---------- mode modal account line ----------------------------------------- */
.mode-account-copy{min-width:0}

/* ---------- AI config apply modal: equal action columns --------------------- */
.ai-config-apply-actions{display:grid;grid-template-columns:1fr 1.2fr}
.ai-config-apply-actions .btn{width:100%}
@media (max-width:700px){
  .ai-config-apply-actions{grid-template-columns:1fr}
}

/* ---------- background library disclosure icon chip ------------------------- */
.background-library-disclosure>summary::before{
  content:"▧";flex:none;display:grid;place-items:center;
  width:25px;height:25px;border-radius:8px;
  border:1px solid var(--accent-line);background:var(--accent-soft);
  color:var(--accent);font-size:.62rem;
}

/* ---------- background action buttons: icon chips + clear variant ----------- */
.background-action-btn .action-icon{
  display:grid;place-items:center;width:22px;height:22px;border-radius:7px;
  border:1px solid var(--accent-line);background:var(--accent-soft);
  color:var(--accent);font-size:.72rem;font-weight:800;
}
.background-action-btn.clear .action-icon{
  border-color:color-mix(in srgb,var(--gold) 30%,transparent);
  background:color-mix(in srgb,var(--gold) 10%,transparent);
  color:var(--gold);
}

/* ---------- cloud save loading veil -----------------------------------------
   shown while bootstrapCloudState() reads the remote snapshot so the user
   never stares at an empty page after the mode modal closes */
.cloud-load-veil{
  position:fixed;inset:0;z-index:400;display:grid;place-items:center;
  background:rgba(4,6,9,.72);
  backdrop-filter:blur(10px) saturate(1.1);-webkit-backdrop-filter:blur(10px) saturate(1.1);
}
.cloud-load-card{
  display:flex;flex-direction:column;align-items:center;gap:7px;
  padding:26px 36px;border-radius:18px;border:1px solid var(--line-2);
  background:linear-gradient(170deg,rgba(24,30,40,.96),rgba(12,15,21,.97));
  box-shadow:var(--shadow-2);
}
.cloud-load-card strong{font-size:.9rem;font-weight:800;letter-spacing:.02em}
.cloud-load-card small{font-size:.66rem;color:var(--tx-3)}
.cloud-load-spinner{
  width:26px;height:26px;border-radius:50%;margin-bottom:4px;
  border:3px solid rgba(255,255,255,.14);border-top-color:var(--accent);
  animation:cloudLoadSpin .8s linear infinite;
}
@keyframes cloudLoadSpin{to{transform:rotate(360deg)}}

/* ---------- desktop HUD vertical clamp (stress heights) --------------------
   The HUD is bottom-anchored; on stages shorter than the rail + HUD stack
   (1366x768, h×0.75 variants) a fixed bottom pushes the community row up
   through the seat cards. Clamp so the HUD top never enters the rail band.
   Lives here (final file) so it wins over every earlier bottom override. */
@media (min-width:1001px){
  .table-center-hud{bottom:clamp(6px, min(30px, calc(100% - var(--player-rail-h) - 242px)), 30px)}
}

/* ---------- short desktop heights: compaction + scroll fallback ------------
   Fixed chrome (topbar 72 + console 329 + gaps 44 = 445px) starves the stage
   below ~866px viewport height, so the bottom-anchored HUD rides up through
   the seat rail. Two tiers:
   A) ≤870px — compact rail/console/HUD so 768–870px fits with zero overlap;
   B) ≤745px — compaction alone cannot fit: let the page scroll vertically
      with a stage floor (standard responsive behaviour, nothing occluded). */
@media (min-width:1001px) and (max-height:870px){
  html{font-size:calc(14.6px * var(--font-scale))}
  .topbar{min-height:48px;padding:5px 12px}
  .game-shell{padding:8px 10px;gap:10px}
  .table-wrap{gap:8px}
  .table-stage{--player-rail-h:calc(208px * (1 + (var(--ui-widen) - 1) * .32))}
  /* short AND narrow: the ≤1180 rule stacks the expression picker under the
     hero card (+~90px console), which starves the stage again — keep it beside */
  .hero-info{grid-template-columns:minmax(0,1fr) calc(200px * var(--ui-widen))}
  .expression-picker{flex-direction:column;align-items:stretch;gap:7px}
  .expression-current{flex:none}
  .expression-options{flex:1}
  .hero-card-header{padding:8px 12px}
  .hero-card-main{padding:7px 10px}
  .hero-ledger-cell{padding-top:3px;padding-bottom:3px}
  .hero-hand .card{--cw:44px;--ch:62px;--crank:1rem;--csuit:1.02rem}
  .hero-stat-grid .hero-stat{padding:4px 6px 5px}
  .controls{padding-top:8px;padding-bottom:8px}
  .action-btn{min-height:40px}
  .raise-box{min-height:40px}
  .expression-picker{padding:8px 10px}
  .table-center-hud{row-gap:3px}
  .street-badge{padding:4px 14px;font-size:.7rem}
  .action-track{padding:3px 10px;font-size:.6rem}
  .community{min-height:66px}
  .community .card{--cw:48px;--ch:68px;--crank:1.1rem;--csuit:1.1rem}
  .pot-box{padding:5px 16px;margin-top:2px}
}
@media (min-width:1001px) and (max-height:745px){
  body{overflow-y:auto}
  .app{height:auto;min-height:100vh;min-height:100dvh}
  .table-zone,.table-layout{height:auto}
  .table-stage{min-height:386px}
}
@media (min-width:1001px) and (max-height:745px){
  /* sidebar content is taller than the compact stage — cap it or it stretches
     the whole shell row to ~1000px */
  .sidebar{max-height:calc(100dvh - 84px)}
}

/* ---------- narrow-desktop min-content floor -------------------------------
   At ~1025px wide the shell still measured 1128px: the rail/HUD min-content
   forced the grid wide and the topbar stretched with it, producing real
   horizontal overflow. Cap the track and let the rail scroll inside it. */
@media (min-width:1001px) and (max-width:1180px){
  .game-shell{grid-template-columns:minmax(0,1fr) calc(260px * var(--ui-widen))}
  .table-zone{overflow:hidden}
  .table-center-hud{min-width:0;width:min(92%,560px)}
  .controls{grid-template-columns:calc(96px * var(--ui-widen)) calc(112px * var(--ui-widen)) minmax(120px,1fr) calc(150px * var(--ui-widen));gap:7px}
}

/* ---------- short portrait phones: keep the felt band inside the stage -----
   iPhone-SE-class heights (≤650px visible — Safari toolbar variants 611/579)
   leave the stage shorter than rail + HUD content, and .table-stage's
   overflow:hidden hard-clips the pot pill and the community card bottoms
   (rect checks still pass — the elements are in the viewport, just clipped).
   Compact the rail, the felt band and the hero console so the whole band
   fits inside the stage. Seat insets, felt top, HUD padding and the dot
   strip all key off --mobile-rail-h, so they follow automatically. */
@media (max-width:700px) and (max-height:650px){
  .table-stage{--mobile-rail-h:118px;--player-rail-h:118px}
  #seats{padding:8px 10px 12px;gap:6px}
  .player-card-head{padding:4px 8px 3px}
  .avatar{width:26px;height:26px;border-radius:8px;font-size:.68rem}
  .mobile-player-snapshot{margin:0 6px 5px;padding:4px 6px;gap:6px}
  .mini-cards{min-height:27px}
  .mini-cards .card{--cw:19px;--ch:27px}
  .mobile-player-core{gap:2px}
  .mobile-player-side .expression-display{width:28px;height:28px}
  .mobile-player-side .expression-display .face{font-size:.8rem}
  .table-center-hud{row-gap:3px;padding:0 6px}
  .action-track{margin-bottom:4px;padding:2px 9px}
  .community{min-height:50px}
  .community .card{--cw:34px;--ch:48px;--cr:5px;--crank:.8rem;--csuit:.8rem}
  .pot-box{padding:3px 10px}
  .pot-box strong{font-size:.8rem}
  /* hero console gives ~25px back to the stage */
  .hero-card-header{padding:3px 10px 2px}
  .hero-card-main{padding:2px 8px}
  .hero-hand .card{--cw:29px;--ch:41px}
  .hero-ledger-cell{padding:2px 7px}
  .hero-stat{padding:1px 3px 2px}
  .expression-picker{padding:2px 8px}
  .controls{padding:5px 8px}
  .action-btn{min-height:32px}
  .raise-box{min-height:28px}
}

/* ---------- portrait HUD: split short-height overflow top/bottom -----------
   The felt-anchored HUD grid (rows 1fr auto auto auto 1fr) centers the
   action-track|community|pot band whenever the felt has spare room, but on
   iPhone-SE-class heights the band is taller than the felt: the 1fr rows
   clamp to content, the grid top-anchors, and the whole deficit dumps onto
   the pot — .table-stage(overflow:hidden) then clips it (measured: 5px at
   375x667, 33px at 375x579). align-content:center is a no-op while free
   space exists, and splits the deficit evenly when there isn't — the top
   half lands in the 10px moat below the rail, the bottom half in the 9px
   moat above the stage edge, so nothing is clipped. */
@media (max-width:1000px) and (orientation:portrait){
  .table-center-hud{align-content:center}
}
/* very short portrait (Safari full chrome, ~579px): shrink the band itself
   so the split deficit never exceeds either moat */
@media (max-width:700px) and (orientation:portrait) and (max-height:600px){
  .community{min-height:42px}
  .community .card{--cw:30px;--ch:42px;--crank:.66rem;--csuit:.66rem}
  .pot-box{padding:1px 8px}
  .pot-box strong{font-size:.74rem}
  .action-track{margin-bottom:4px}
}

/* ---------- fullscreen victory celebration (JS: #victoryFx canvas) ---------
   Non-blocking 2s firework overlay; theme personas retint it via --vfx-1..3. */
#victoryFx{position:fixed;inset:0;z-index:520;pointer-events:none;background:rgba(4,6,10,.16);transition:opacity .42s ease}
#victoryFx canvas{width:100%;height:100%;display:block}
#victoryFx.fade{opacity:0}
#victoryFx.glow-only{background:radial-gradient(circle at 50% 42%,color-mix(in srgb,var(--gold) 26%,transparent),rgba(4,6,10,.12) 62%)}

/* ---------- mobile setup panels: collapsible 01/02/03 (JS toggles .collapsed)
   Caret only exists on ≤1000px; desktop never shows it and .collapsed is
   stripped by syncSetupPanelCollapse, so the desktop flow is unchanged. */
.setup-caret{display:none}
@media (max-width:1000px){
  /* The game surface owns vertical swipes so Android does not cancel the
     pointer stream before the table-info drawer threshold is reached. */
  .game-shell{touch-action:pan-x!important}
  #setupCorePanel>.setup-panel-head,.setup-style-panel>.setup-panel-head,.setup-ai-panel>.setup-panel-head{cursor:pointer;user-select:none;-webkit-tap-highlight-color:transparent}
  #setupCorePanel>.setup-panel-head:active,.setup-style-panel>.setup-panel-head:active,.setup-ai-panel>.setup-panel-head:active{opacity:.82}
  .setup-caret{display:grid;place-items:center;flex:none;width:22px;height:22px;margin-left:8px;border-radius:7px;border:1px solid var(--accent-line);background:var(--accent-soft);color:var(--accent);font-size:.6rem;transition:transform .2s ease}
  .setup-caret::before{content:"▾"}
  .setup-panel.collapsed>.setup-panel-head .setup-caret{transform:rotate(-90deg)}
  .setup-panel.collapsed>*:not(.setup-panel-head){display:none}
}

/* ---------- phone landscape: wager pill clears the status icons ------------
   横屏1：筹码胶囊原来 bottom:-6px，18px 高的胶囊覆盖卡片底部 13px，正好压住
   行动状态图标行；下移后与卡片底边只剩 8px 交叠，图标完整可见。
   横屏2：hero 右列在较大字体下溢出 5–7px（隐藏滚动条 = “显示不全”），
   通过压缩间距/内边距腾出 ~14px 头空间，各档字体均无需滚动。 */
@media (max-width:1000px) and (orientation:landscape),(max-height:540px) and (orientation:landscape) and (pointer:coarse){
  .seat-wager{bottom:-11px}
  .hero-panel{gap:3px}
  .hero-info{gap:3px}
  .hero-card-header{padding:4px 9px 3px}
  .hero-card-main{padding:4px 7px}
  .hero-stat{padding:2px 2px 3px}
  .hero-ledger-cell{padding-top:1px;padding-bottom:1px}
  .expression-btn{min-height:26px}
}
/* ==========================================================================
   12 · CIRCULAR RAIL CAROUSEL — transform-driven loop, position dots
   Loaded LAST so these rules win over the seat rules in 03/08/09.
   The rail is no longer a scroll container: JS positions every card by
   transform (see the carousel block in index.html), wrapping offsets so the
   cards loop infinitely. Widths still come from 03/08/09 per breakpoint.
   ========================================================================== */

#seats{
  overflow:hidden!important;
  touch-action:pan-y; /* vertical page scroll survives; horizontal = our drag */
  --rail-gap:8px;
  cursor:grab;
  mask-image:linear-gradient(90deg,transparent,#000 22px,#000 calc(100% - 22px),transparent);
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 22px,#000 calc(100% - 22px),transparent);
  /* 拖动信息栏时不再拖选文本 */
  user-select:none;-webkit-user-select:none;-webkit-touch-callout:none;
}
#seats .seat,#seats .seat *{user-select:none;-webkit-user-select:none}
#seats.dragging{cursor:grabbing}
/* top is !important: seat markup carries an inline top:% from the orbit
   layout that must lose to the carousel's vertical insets. The insets keep
   the role-strip (-11px) and wager pill (-17px) inside the clipped rail. */
#seats .seat{
  position:absolute!important;top:11px!important;bottom:19px;
  margin:0;will-change:transform;
}

/* mobile portrait rail: tighter insets (role-strip -9px, wager -6px) */
@media (max-width:1000px){
  #seats{
    mask-image:linear-gradient(90deg,transparent,#000 12px,#000 calc(100% - 12px),transparent);
    -webkit-mask-image:linear-gradient(90deg,transparent,#000 12px,#000 calc(100% - 12px),transparent);
  }
  #seats .seat{top:9px!important;bottom:20px}
}

/* mobile landscape rail (role-strip -7px, wager pill -16px + +N lane 8px) */
@media (max-width:1000px) and (orientation:landscape),(max-height:540px) and (orientation:landscape) and (pointer:coarse){
  #seats .seat{top:8px!important;bottom:26px}
}

/* the old drag-hint pill is retired — the dot strip is the position UI */
.mobile-rail-hint{display:none!important}

/* ---------- position dots + nudge arrows under the rail ------------------ */
.rail-nav{
  position:absolute;left:50%;top:calc(var(--player-rail-h) + 9px);
  transform:translateX(-50%);z-index:24;
  display:flex;align-items:center;gap:9px;pointer-events:auto;
}
.rail-dots{display:flex;align-items:center;gap:6px}
.rail-dot{
  width:7px;height:7px;padding:0;border:0;border-radius:99px;cursor:pointer;
  background:rgba(255,255,255,.22);
  transition:width var(--t-fast),background var(--t-fast),box-shadow var(--t-fast);
}
.rail-dot:hover{background:rgba(255,255,255,.45)}
/* 局后加载：整排圆点失去 active（全灰）时视为载入中，阶梯跳动提示进度。
   :has 自带同步——active 一恢复动画即停，无需 JS 状态接线 */
@keyframes railDotJump{0%,60%,100%{transform:translateY(0)}30%{transform:translateY(-6px)}}
.rail-dots.loading .rail-dot,.rail-dots:not(:has(.rail-dot.active)) .rail-dot{animation:railDotJump 1.05s ease-in-out infinite}
.rail-dots.loading .rail-dot:nth-child(2),.rail-dots:not(:has(.rail-dot.active)) .rail-dot:nth-child(2){animation-delay:.09s}
.rail-dots.loading .rail-dot:nth-child(3),.rail-dots:not(:has(.rail-dot.active)) .rail-dot:nth-child(3){animation-delay:.18s}
.rail-dots.loading .rail-dot:nth-child(4),.rail-dots:not(:has(.rail-dot.active)) .rail-dot:nth-child(4){animation-delay:.27s}
.rail-dots.loading .rail-dot:nth-child(5),.rail-dots:not(:has(.rail-dot.active)) .rail-dot:nth-child(5){animation-delay:.36s}
.rail-dots.loading .rail-dot:nth-child(6),.rail-dots:not(:has(.rail-dot.active)) .rail-dot:nth-child(6){animation-delay:.45s}
.rail-dots.loading .rail-dot:nth-child(7),.rail-dots:not(:has(.rail-dot.active)) .rail-dot:nth-child(7){animation-delay:.54s}
.rail-dots.loading .rail-dot:nth-child(8),.rail-dots:not(:has(.rail-dot.active)) .rail-dot:nth-child(8){animation-delay:.63s}
.rail-dots.loading .rail-dot:nth-child(9),.rail-dots:not(:has(.rail-dot.active)) .rail-dot:nth-child(9){animation-delay:.72s}
.rail-dots.loading .rail-dot:nth-child(10),.rail-dots:not(:has(.rail-dot.active)) .rail-dot:nth-child(10){animation-delay:.81s}
.rail-dot.active{width:18px;background:var(--gold);box-shadow:0 0 10px rgba(237,174,73,.55)}
/* 弃牌/淘汰玩家：镂空圆点，与在局玩家的实心点区分 */
.rail-dot.folded{background:transparent;box-shadow:inset 0 0 0 1.4px rgba(255,255,255,.32)}
.rail-dot.folded:hover{box-shadow:inset 0 0 0 1.4px rgba(255,255,255,.55)}
.rail-dot.folded.active{background:transparent;box-shadow:inset 0 0 0 1.6px var(--gold),0 0 10px rgba(237,174,73,.55)}
.rail-nav-btn{
  display:none;width:22px;height:22px;border-radius:99px;
  border:1px solid var(--line-2);background:rgba(8,11,15,.82);color:var(--tx-2);
  font-size:.8rem;line-height:1;cursor:pointer;align-items:center;justify-content:center;
  transition:color var(--t-fast),border-color var(--t-fast);
}
.rail-nav-btn:hover{color:var(--tx-1);border-color:var(--line-3)}
/* arrows only appear when the ring actually overflows (desktop scroll affordance) */
.rail-nav.rail-loop .rail-nav-btn{display:inline-flex}

@media (max-width:1000px){
  .rail-nav{top:calc(var(--mobile-rail-h) + 1px);gap:6px}
  .rail-nav-btn{display:none!important} /* phones drag; arrows would crowd the felt */
  .rail-dot{width:6px;height:6px}
  .rail-dot.active{width:14px}
}

/* mobile raise-stepper containment
   Keep the numeric arrows inside the compact raise field at every phone size. */
@media (max-width:1000px){
  .raise-input-shell{height:calc(100% - 2px);max-height:28px;overflow:hidden;border-radius:9px}
  .raise-stepper{top:1px;bottom:1px;right:3px;transform:none;width:20px;gap:0;justify-content:space-between}
  .raise-step-btn{width:20px;height:50%;min-height:0}
}
