/* ==========================================================================================
   devices.css — multi-device layer (loaded last). Desktop Chrome / Edge / Firefox / Safari, iPad and Android tablets
   in landscape, phones in landscape (≥ 640×360 CSS px). js/ui/device.js puts the feature classes on <html>
   (sp-touch, sp-coarse, sp-hover / sp-no-hover, sp-fs, sp-standalone, sp-reduced-motion) — never UA sniffing.

   1. no page zoom / scroll / pull-to-refresh while playing (touch-action, overscroll-behavior, callouts)
   2. safe-area insets (notches, rounded corners, home indicator): the HUD and every screen stay inside them while the
      board and the backdrops stay full-bleed
   3. touch: every small control gets an invisible ≥ 44 px hit area (::before, no layout change); on short touch
      screens (phones) the most used controls also grow visually
   4. reduced motion extras; fullscreen button
   ========================================================================================== */

:root {
  --sa-t: env(safe-area-inset-top, 0px);
  --sa-r: env(safe-area-inset-right, 0px);
  --sa-b: env(safe-area-inset-bottom, 0px);
  --sa-l: env(safe-area-inset-left, 0px);
  --tap-min: 44px;
}

/* ---- 1. gestures ------------------------------------------------------------------------------------------------- */
html, body { overscroll-behavior: none; }
/* pan only: no pinch-zoom / double-tap zoom anywhere (scrollable lists still scroll); the field canvas uses none */
html { touch-action: pan-x pan-y; }
body { -webkit-touch-callout: none; }
button, a, label, summary, [role="button"], [role="tab"], [role="option"], .scard, .lvcard, .dband, .bond, .pavatar { touch-action: manipulation; }
img { -webkit-user-drag: none; }
.gm__field, .gm__field canvas, .ff, .fwheel, .fwheel__dia { touch-action: none; }
/* the DOM fallback board drags with pointer events too */
.ff-piece, .ff-slot, .ff-tile { touch-action: none; -webkit-user-select: none; user-select: none; }
input, textarea, select { -webkit-user-select: text; user-select: text; }

/* ---- 2. safe areas ------------------------------------------------------------------------------------------------ */
/* the in-match HUD layer sits inside the safe area; the field (canvas) below it stays full-bleed */
.gm__hud { top: var(--sa-t); right: var(--sa-r); bottom: var(--sa-b); left: var(--sa-l); }
/* the selected unit's underframe (ui/underframe.js) is placed at the tile's CLIENT px (view.tileScreen) inside the HUD
   layer: step back over the HUD's safe-area offset so it stays on its unit on a notched phone */
.gm__hud > .uframe { margin-left: calc(-1 * var(--sa-l)); margin-top: calc(-1 * var(--sa-t)); }
/* the shop bar keeps its place at the bottom edge (it may reach into the home-indicator strip): raised by the bottom
   inset it covered ~3/4 of the bench pads 2–9 on a notched phone (user playtest #4 item 1), and a bench piece is
   pressed / dropped on by its tile. The prep camera keeps the bench clear of the bar (ui/fieldHost.js hudBands →
   render/projection.js clearHud, user playtest #5 item 9). Its right edge stays inside the inset (the notch / rounded
   corner). */
.gm__hud > .shopbar { bottom: calc(.2rem - var(--sa-b)); }
.gm > .spov { left: calc(2.8rem + var(--sa-l)); right: var(--sa-r); }
/* every other screen is laid out inside the safe area (the app backdrop fills the notch strip) */
.screen:not(.gm) { top: var(--sa-t); right: var(--sa-r); bottom: var(--sa-b); left: var(--sa-l); }
.guide, .awayov { padding: var(--sa-t) var(--sa-r) var(--sa-b) var(--sa-l); }
/* (in a match the toasts sit under the top bar: components.css .sp-in-match .toast-host already adds --sa-t) */
html:not(.sp-in-match) .toast-host { margin-top: var(--sa-t); }
.conn-banner { margin-bottom: var(--sa-b); }
.rotate-hint { padding: calc(24px + var(--sa-t)) calc(24px + var(--sa-r)) calc(24px + var(--sa-b)) calc(24px + var(--sa-l)); }

/* ---- 3. touch targets ---------------------------------------------------------------------------------------------- */
/* An invisible hit area of at least --tap-min around small controls on touch screens: centred, never smaller than the
   control, no layout change. Only controls that do not use ::before themselves are listed. */
:where(.sp-coarse) :where(.tapx, .gtop__exit, .gtop__iconbtn, .enemybtn, .readybtn, .gm__gear, .fsbtn, .ewheel__btn, .ewheel__nav,
  .toolbtn, .funds__collapse, .team__btn, .effect, .bslot .bond, .vswitch__arrow, .edrawer__close, .dpanel__close, .bpop__close,
  .modal__close, .uf__btn, .chud__back, .tabs__tab, .btn, .pill-btn, .title-guide, .stephead__guide, .set-toggle, .set-seg button) {
  position: relative;
}
.sp-coarse :is(.tapx, .gtop__exit, .gtop__iconbtn, .enemybtn, .readybtn, .gm__gear, .fsbtn, .ewheel__btn, .ewheel__nav,
  .toolbtn, .funds__collapse, .team__btn, .effect, .bslot .bond, .vswitch__arrow, .edrawer__close, .dpanel__close, .bpop__close,
  .modal__close, .uf__btn, .chud__back, .tabs__tab, .btn, .pill-btn, .title-guide, .stephead__guide, .set-toggle, .set-seg button)::before {
  content: '';
  position: absolute; left: 50%; top: 50%; z-index: 0;
  width: max(100%, var(--tap-min)); height: max(100%, var(--tap-min));
  transform: translate(-50%, -50%);
  background: transparent; border: 0; box-shadow: none; clip-path: none; pointer-events: auto;
}
/* The 交流 panel's pager (‹ dots ›) sits in the strip under the emote cells (panel y 1.97–2.31rem, css/emotes.css). Its
   hit areas stay inside that strip and never overlap each other: a centred 44 px area of ‹ / › covered the centres of
   the bottom-left / bottom-right emotes (a tap there turned the page or hit the disabled ‹), and 28 px dot areas 0.21rem
   apart covered the neighbouring dot (a tap on dot N opened page N+1). ‹ / › grow outwards (away from the dots), the
   dots only up to half the gap. Selectors ≥ the specificity of the generic hit-area rule above. */
.sp-coarse .ewheel__panel .ewheel__nav::before { top: .03rem; bottom: -.15rem; height: auto; transform: none; }
.sp-coarse .ewheel__panel .ewheel__nav.is-prev::before { left: auto; right: 0; }
.sp-coarse .ewheel__panel .ewheel__nav.is-next::before { left: 0; right: auto; }
.sp-coarse .ewheel__dot::before { inset: -.02rem -.05rem -.2rem; width: auto; height: auto; transform: none; }

/* Phones in landscape (short touch screens: 1rem ≈ 40 px here): the most used controls grow a little so they are
   comfortable to hit and to read, without changing the official layout. */
@media (max-height: 600px) and (pointer: coarse), (max-height: 600px) and (any-pointer: coarse) {
  .readybtn { height: max(.56rem, 34px); }
  .toolbtn { min-height: max(.5rem, 30px); }
  .gm__gear { width: max(.56rem, 34px); height: max(.56rem, 34px); }
  /* ⚙ / 📖 / ⛶ keep their desktop proportion (half the button) instead of an 11 px glyph in the grown button */
  .gm__corner .gm__gear .icon { width: max(.28rem, 17px); height: max(.28rem, 17px); }
  .gtop__exit { min-width: max(.56rem, 34px); min-height: max(.56rem, 34px); }
  .gtop__iconbtn.has-sprite, .enemybtn.has-sprite { height: max(.46rem, 26px); width: calc(max(.46rem, 26px) * 35 / 29); }
  .gtop__iconbtn.has-sprite.is-wide, .enemybtn.has-sprite.is-wide { width: calc(max(.46rem, 26px) * 61 / 29); }
  .gtop__iconbtn:not(.has-sprite), .enemybtn:not(.has-sprite) { height: max(.46rem, 26px); min-width: max(.46rem, 26px); }
  .ewheel__panel { scale: 1.3; transform-origin: 0 100%; }
  .fwheel__cancel { min-height: 30px; }
  .set-toggle, .set-seg button { height: max(.38rem, 30px); }
  /* a taller range input whose visible track stays thin: transparent borders are part of the hit area */
  .set-range { height: 30px; border: 11px solid transparent; border-left-width: 0; border-right-width: 0; background-clip: padding-box; box-shadow: inset 0 0 0 1px var(--line-2); }
  .field--lg .field__box { height: max(.68rem, 40px); }
}

/* ---- 4. misc ------------------------------------------------------------------------------------------------------ */
.fsbtn { display: grid; place-items: center; padding: 0; background: rgba(8, 11, 10, .85); border: 1px solid var(--line-2); color: var(--text-lo); transition: color var(--t-fast), border-color var(--t-fast); }
.fsbtn .icon { width: .26rem; height: .26rem; }
.fsbtn:hover, .fsbtn[aria-pressed="true"] { color: var(--mint-400); border-color: var(--mint-700); }
.title-fs, .title-settings { width: .4rem; height: .4rem; margin-left: .06rem; }
/* standalone / fullscreen: the browser bars are gone, nothing to reserve */
.sp-standalone .rotate-hint__micro::after { content: ' · PWA'; }

/* reduced motion: camera-free UI pieces that animate continuously stop (the global rule in theme.css shortens the rest) */
@media (prefers-reduced-motion: reduce) {
  .team__status.is-gold, .ff-tile--gate, .title-bg__sweep, .boot__hex { animation: none !important; }
}
.sp-reduced-motion .team__status.is-gold, .sp-reduced-motion .ff-tile--gate { animation: none; }

/* (the shop cards have no ⓘ corner: its invisible 44 px touch area — the 30 px override here lost on specificity to the
   generic rule above — covered the bottom-right quarter of a phone's 67 × 97 px card and half of its 确认购买 strip, so
   the confirming tap re-opened the detail instead; user playtest #6 item 10) */

/* Narrow viewports (< 768 CSS px: the root scale is clamped at 40 px, so the layout is narrower than the 19.2 rem
   design): the shop bar would cover the corner's ⚙ / 📖 / ⛶ buttons — they wrap into a row above the 交流 button. */
/* The left-docked detail card then ends above that two-row corner (.24rem + 交流 + .1rem gap + ⚙ row + .1rem): at its
   desktop bottom (1.02rem) it hid ⚙ / 📖 / ⛶. (gameLogic panelSlots keeps the taller desktop slot: conservative.) */
@media (max-width: 767px) {
  .gm__corner { flex-wrap: wrap-reverse; align-content: flex-start; width: max(72px, calc(3 * .56rem + .2rem)); }
  .dpanel:not(.dpanel--right) { bottom: calc(.44rem + max(1.51rem, 72px) * 55 / 151 + .56rem); }
}
@media (max-width: 767px) and (pointer: coarse), (max-width: 767px) and (any-pointer: coarse) {
  .gm__corner { width: max(96px, calc(3 * max(.56rem, 34px) + .2rem)); }
  .dpanel:not(.dpanel--right) { bottom: calc(.44rem + max(1.51rem, 96px) * 55 / 151 + max(.56rem, 34px)); }
}
/* With the shop folded (收起) no bar covers the corner: one row again, so the folded shop's prep camera, which keeps the
   bench above the corner's hit areas (ui/fieldHost.js hudBands), grows the board on narrow phones too (public issue #5). */
@media (max-width: 767px) {
  .gm.is-collapsed .gm__corner { flex-wrap: nowrap; width: auto; }
}

/* Short landscape phones (≤ 460 CSS px tall, 1rem = 40 px): the lobby's two columns are compressed so the
   创建同盟 button and the join panel stay on screen without scrolling. The detail card's header is shorter (no
   romanised name, a shorter portrait) so the operator's own effect (特质) under it stays in view even in the short card
   docked above the shop bar (user playtest #3 item 8). */
@media (max-height: 460px) {
  .dhead__art { height: 1.66rem; }
  .dhead__en { display: none; }
  .lobby-screen .topbar { height: 48px; }
  .lobby-body { grid-template-columns: repeat(2, minmax(0, 1fr)); padding: 8px 16px; gap: 12px; }
  .lobby-screen .section-label { font-size: 13px; margin-bottom: 8px; min-height: 20px; gap: 6px; }
  .lobby-screen .section-label > .micro { display: none; }
  .lobby-protocol-help { height: 28px; font-size: 12px; padding-inline: 8px; }
  .mode-cards { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; margin-bottom: 12px; }
  .lobby-screen .mode-card { min-height: 64px; padding: 8px 4px; gap: 6px; align-items: center; justify-content: center; }
  .lobby-screen .mode-card--match { grid-column: auto; flex-direction: column; }
  .lobby-screen .mode-card__icon { width: 24px; height: 24px; }
  .lobby-screen .mode-card__icon .icon { width: 18px; height: 18px; }
  .lobby-screen .mode-card__name { font-size: 14px; letter-spacing: .02em; }
  .lobby-screen .mode-card__text { align-items: center; gap: 0; }
  .lobby-screen .mode-card__text > .micro, .lobby-screen .mode-card__desc, .lobby-screen .mode-card__points,
  .lobby-screen .mode-card__check, .lobby-screen .diff-card__effects, .tips, .create-box__hint { display: none; }
  .lobby-screen .join-panel .panel__body { padding: 10px; gap: 6px; }
  .lobby-screen .join-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 6px; }
  .lobby-screen .join-row .field { grid-column: 1 / -1; }
  .lobby-screen .join-row .btn { height: 40px; min-height: 40px; font-size: 13px; padding-inline: 12px; }
  .lobby-screen .join-row .join-spectate { width: 100%; }
  .lobby-screen .join-foot { min-height: 20px; flex-wrap: nowrap; gap: 6px; font-size: 11px; }
  .lobby-screen .join-foot > .t-lo { display: none; }
  .lobby-screen .code-chip { font-size: 12px; padding: 4px 6px; letter-spacing: .08em; }
  .lobby-screen .diff-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; }
  .lobby-screen .diff-card { min-height: 58px; padding: 8px 8px 8px 12px; gap: 4px; }
  .lobby-screen .diff-card__head { flex-wrap: wrap; gap: 3px 5px; }
  .lobby-screen .diff-card__name { font-size: 14px; }
  .lobby-screen .diff-card__meta { width: 100%; font-size: 10px; margin: 0; }
  .lobby-screen .diff-card__meta .num { font-size: 11px; }
  .lobby-screen .diff-card__hidden { font-size: 10px; }
  .lobby-screen .diff-card__desc, .lobby-screen .diff-card__code, .lobby-screen .diff-card__check { display: none; }
  .lobby-screen .create-box { margin-top: 10px; }
  .lobby-screen .create-box .btn--xl { height: 44px; font-size: 16px; }
  .lobby-screen .matchmaking--compact { margin-top: 8px; padding: 8px 10px; }
  .lobby-screen .matchmaking--compact > header, .lobby-screen .matchmaking--compact:not(.is-active) > h2 { display: none; }
  .lobby-screen .matchmaking h2 { font-size: 17px; margin: 0 0 4px; }
  .lobby-screen .matchmaking p { font-size: 12px; line-height: 1.35; margin: 3px 0; }
  .lobby-screen .matchmaking__status { font-size: 12px; line-height: 1.4; margin: 4px 0 6px; }
  .lobby-screen .matchmaking__actions { gap: 6px; }
  .lobby-screen .matchmaking__actions .btn { height: 44px; min-height: 44px; font-size: 12px; padding-inline: 6px; letter-spacing: 0; }
  .lobby-screen .matchmaking__revival { font-size: 12px; padding: 5px 8px; margin: 6px 0; }
  .lobby-screen .matchmaking__revival p { font-size: 11px; line-height: 1.4; margin: 0; }
  .lobby-screen .matchmaking__count { font-size: 11px; }
  .lobby-dialog .modal__head { padding: 12px 16px 4px; }
  .lobby-dialog .modal__body { padding: 8px 16px 12px; }
  .lobby-dialog .modal__actions { padding: 8px 16px; }
  .lobby-dialog .modal__actions .btn { min-height: 44px; font-size: 14px; }
  .lobby-dialog .micro { font-size: 10px; }

}

.rotate-hint__pwa { pointer-events: auto; margin-top: 6px; }
.rotate-hint__pwa[hidden] { display: none; }

/* Keep lobby actions within their real rows; expanded touch rosters start folded. */
@media (max-height: 600px) and (pointer: coarse), (max-width: 767px) {
  .lobby-screen .topbar { height: auto; min-height: 48px; padding-block: 4px; }
  .lobby-screen .topbar__left { flex-wrap: nowrap; }
  .lobby-screen .topbar__right { min-width: 0; }
  .lobby-screen .online-players, .lobby-screen .me-chip { display: none; }
  .lobby-screen .topbar .btn, .lobby-tools > summary { min-height: 44px; }
  .lobby-body { padding-block: 6px; }
  .gm .team { top: max(2.6rem, 104px); bottom: 88px; width: min(340px, calc(100vw - 32px)); max-height: calc(100% - 192px); padding: 4px; gap: 4px; }
  .gm .team.is-folded { bottom: auto; width: 164px; }
  .team .team__players { min-height: 0; overflow-y: auto; overflow-x: hidden; pointer-events: auto; touch-action: pan-y; overscroll-behavior: contain; }
  .gm .team:not(.team--expanded) .team__row { min-height: 56px; gap: 10px; padding: 4px; background: rgba(10, 18, 15, .9); }
  .gm .team .team__btn { min-width: 44px; min-height: 44px; }
  .gm .team .team__name { font-size: max(calc(.16 * var(--t)), 14px); max-width: 100%; }
  .gm .team .lp--sm { font-size: 14px; }
  .gm .team .team__ob, .gm .team .team__back { min-height: 44px; position: static; font-size: 14px; }
  .gm .scard__name, .gm .lvcard__label, .gm .toolbtn, .gm .readybtn__label { font-size: max(.19rem, 12px); }
  .gm .scard__class, .gm .scard__bond, .gm .scard__idesc, .gm .lvcard__micro, .gm .funds__label, .gm .shopbar__remain { font-size: max(.13rem, 10px); }
  .gm .funds__collapse, .gm .shopbar-tab__btn { min-height: 44px; font-size: 12px; }
  .gm .chud__prog, .gm .chud__pname { font-size: 12px; }
  .gm .chud__progress { max-height: 96px; overflow-y: auto; pointer-events: auto; touch-action: pan-y; }
  .gm .chud__prog .num { font-size: 12px; }
  .set-row__label { font-size: max(calc(.17 * var(--t)), 13px); }
  .set-row .set-toggle, .set-row .set-seg button { min-height: 44px; font-size: 12px; }
  .set-row { gap: 10px; }
  .set-hint { font-size: max(calc(.14 * var(--t)), 12px); }
}
@media (max-width: 600px) and (min-height: 461px) {
  .lobby-screen .topbar { min-height: 96px; }
  .lobby-screen .topbar__right { justify-content: flex-start; }
}
@media (max-height: 460px) and (min-width: 601px) {
  .lobby-screen .topbar { grid-template-columns: auto minmax(0, 1fr); }
  .lobby-screen .topbar__right { flex-wrap: nowrap; justify-content: flex-end; }
  .lobby-screen .ping--load { max-width: 88px; }
  .lobby-screen .mode-card { min-height: 56px; }
  .lobby-screen .diff-card { min-height: 54px; }
  .lobby-screen .join-panel .panel__body { padding: 6px; }
  .lobby-screen .join-foot { min-height: 0; }
  .lobby-screen .join-foot > .t-dim { display: none; }
}
