/* ==========================================================================================
   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 { 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)); }
}

/* 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-body { padding-top: .2rem; padding-bottom: .2rem; gap: .36rem; }
  .mode-cards { margin-bottom: .2rem; }
  .mode-card { min-height: 0; gap: .12rem; padding: .22rem .26rem .2rem; }
  .mode-card__icon { width: .66rem; height: .66rem; }
  .mode-card__name { font-size: .4rem; }
  .mode-card__points, .diff-card__effects, .tips, .create-box__hint { display: none; }
  .diff-list { gap: .1rem; }
  .diff-card { padding-top: .12rem; padding-bottom: .12rem; }
  .create-box { margin-top: .18rem; }
  .create-box .btn--xl { height: .8rem; }
}
