/* PWA and cross-device styling: install/update banners, standalone chrome,
   safe-area handling for notched phones, and touch ergonomics for the board. */

/* ---------------------------------------------------------------- banners */
.pwa-banner {
  position: fixed;
  left: 50%;
  bottom: calc(18px + env(safe-area-inset-bottom));
  transform: translate(-50%, 160%);
  z-index: 120;
  display: flex;
  align-items: center;
  gap: 14px;
  max-width: min(520px, calc(100vw - 28px));
  padding: 13px 16px;
  border-radius: 16px;
  background: linear-gradient(180deg, rgba(17, 34, 46, .98), rgba(9, 20, 29, .98));
  border: 1px solid rgba(108, 145, 163, .28);
  box-shadow: 0 18px 44px rgba(0, 0, 0, .5);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  color: var(--text, #eef4f7);
  transition: transform .32s cubic-bezier(.22, 1, .36, 1);
}
.pwa-banner.show { transform: translate(-50%, 0); }
.pwa-banner b { display: block; font-size: 13.5px; }
.pwa-banner small { display: block; color: var(--muted, #8da3b1); font-size: 11.5px; margin-top: 2px; }
.pwa-banner img { border-radius: 10px; flex: 0 0 auto; }
.pwa-banner.warn { border-color: rgba(240, 120, 120, .45); }
.pwa-banner.ok { border-color: rgba(98, 209, 143, .45); }

.pwa-banner-actions { display: flex; gap: 8px; margin-left: auto; flex: 0 0 auto; }
.pwa-btn {
  font: inherit; font-size: 12.5px; font-weight: 700; cursor: pointer;
  padding: 8px 14px; border-radius: 10px; border: 0; white-space: nowrap;
  background: linear-gradient(135deg, #f7d47b, #d9a93d); color: #17202a;
}
.pwa-btn:hover { filter: brightness(1.07); }
.pwa-btn.ghost {
  background: transparent; color: var(--muted, #8da3b1);
  border: 1px solid rgba(108, 145, 163, .3);
}
.pwa-btn.ghost:hover { color: var(--text, #eef4f7); }

.pwa-dot {
  width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto;
  background: #f07878; box-shadow: 0 0 0 4px rgba(240, 120, 120, .18);
  animation: pwaPulse 1.6s ease-in-out infinite;
}
.pwa-dot.ok { background: #62d18f; box-shadow: 0 0 0 4px rgba(98, 209, 143, .18); animation: none; }
@keyframes pwaPulse { 0%, 100% { opacity: 1 } 50% { opacity: .45 } }

.pwa-steps { text-align: left; margin: 16px 0 20px; padding-left: 20px; line-height: 2; color: var(--muted, #8da3b1); }
.pwa-steps b { color: var(--text, #eef4f7); }

/* ---------------------------------------------------------------- offline state */
body.is-offline .pulse-badge,
body.is-offline .network-dot { background: #f07878 !important; }
body.is-offline .stale-note { display: block; }
.stale-note { display: none; font-size: 11px; color: var(--muted); margin-top: 6px; }

/* ---------------------------------------------------------------- standalone chrome */
/* Installed apps have no browser UI, so the app supplies its own safe padding. */
body.is-standalone .topbar { padding-top: calc(env(safe-area-inset-top) * .5); }
body.is-standalone .sidebar { padding-top: calc(14px + env(safe-area-inset-top)); }
body.is-standalone .mobile-nav { padding-bottom: calc(6px + env(safe-area-inset-bottom)); }
body.is-standalone .content { padding-bottom: calc(20px + env(safe-area-inset-bottom)); }

/* iOS overscroll shows the page background, so it must match the app. */
body.is-ios { background-color: #071019; overscroll-behavior-y: none; }
body.is-ios.light { background-color: #f2f6f8; }

/* Window Controls Overlay: keep the top bar clear of the OS window buttons. */
body.has-wco .topbar {
  padding-left: calc(env(titlebar-area-x, 0px) + 12px);
  padding-right: calc(100% - env(titlebar-area-width, 100%) - env(titlebar-area-x, 0px) + 12px);
  -webkit-app-region: drag;
}
body.has-wco .topbar button,
body.has-wco .topbar input { -webkit-app-region: no-drag; }

#installAppBtn[hidden] { display: none !important; }

/* ---------------------------------------------------------------- touch ergonomics */
@media (pointer: coarse) {
  /* Bigger hit feedback without widening the board: a fixed min-width on the
     points would overflow a 375px viewport, so only the target ring grows. */
  .checker { touch-action: manipulation; }
  .die, .cube { touch-action: manipulation; }
  .btn, .nav-link, .icon-btn, .toolbar-actions button, .match-actions button {
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
  }
  .match-actions button { min-height: 42px; }
  .side-tabs button { min-height: 40px; }
  /* A tapped point needs a clearer target ring than a hovered one. */
  .point.legal-target::after { width: clamp(26px, 7vw, 34px); height: clamp(26px, 7vw, 34px); }
}

/* Stop iOS double-tap zoom from eating the second tap of a move. */
.backgammon-board, .board-stage { touch-action: manipulation; }

/* ---------------------------------------------------------------- overflow guard */
/* The layout is grid-based; grid and flex children default to min-width:auto,
   which lets wide content push the page sideways on a phone. */
html { max-width: 100%; overflow-x: hidden; }
/* 100vh is wrong on mobile browsers with a collapsing address bar. */
@supports (height: 100dvh) { body { min-height: 100dvh; } }
.app-shell, .main, .content, .match-shell, .match-main, .board-stage,
.match-main > *, .match-shell > * { min-width: 0; }
.backgammon-board { max-width: 100%; }
.top-actions { min-width: 0; flex-wrap: wrap; justify-content: flex-end; }
.move-list, .chat-log, .decision-list, .leaderboard-table { overflow-x: auto; }

/* ---------------------------------------------------------------- phones */
@media (max-width: 760px) {
  .match-shell { gap: 10px; }
  /* Keep the top bar to one row: the essentials are the balance and the menu. */
  .topbar { height: auto; min-height: 64px; padding: 8px 12px; }
  .top-actions { gap: 6px; flex-wrap: nowrap; }
  .top-actions .btn.tiny { padding: 5px 9px; font-size: 11px; white-space: nowrap; }
  .credit-pill small { display: none; }
  .credit-pill { padding: 4px 8px; }
  #installAppBtn { display: none; }        /* the install banner covers this */
  .board-toolbar { flex-wrap: wrap; gap: 6px; }
  .toolbar-actions { margin-left: auto; }
  .toolbar-actions button { padding: 5px 8px; font-size: 11px; }
  .backgammon-board { min-height: 300px; height: 44vh; }
  .match-top { flex-wrap: wrap; gap: 8px; }
  .match-center strong { font-size: 22px; }
  .dice-area { gap: 10px; }
  .roll-btn { min-height: 48px; }
  .roll-btn kbd { display: none; }
  .match-actions { overflow-x: auto; scrollbar-width: none; }
  .match-actions::-webkit-scrollbar { display: none; }
  .pwa-banner { flex-wrap: wrap; }
  .pwa-banner-actions { margin-left: 0; width: 100%; justify-content: flex-end; }
}

/* Any short landscape viewport: keep the whole board on screen. */
@media (max-height: 560px) and (orientation: landscape) {
  .backgammon-board { height: min(70vh, 420px); min-height: 0; }
  .content { padding-bottom: 76px; }
  .dice-area { padding: 6px 10px; }
}

/* Landscape phones: the board is the whole point of the screen.

   The shell becomes a flex column: fixed rows (score plate, dice, actions) are
   compacted to their essentials and the board takes everything left over, so a
   360px-tall handset shows the full table without scrolling. */
@media (max-height: 520px) and (orientation: landscape) and (pointer: coarse) {
  .sidebar, .mobile-nav, .topbar, .match-side, .board-toolbar { display: none; }
  .app-shell { grid-template-columns: 1fr; }
  .content { padding: 6px; }

  .match-shell { display: flex; flex-direction: column; gap: 6px; height: calc(100vh - 12px); }
  /* Grid and flex items default to min-height:auto and would ignore the cap. */
  .match-main, .match-main > *, .board-stage { min-height: 0; }

  .match-top { flex: 0 0 auto; padding: 4px 10px; }
  .match-center small { display: none; }
  .match-center strong { font-size: 17px; }
  .match-center em { font-size: 10px; }
  .player-plate b { font-size: 12px; }
  .player-plate img { width: 28px; height: 28px; }

  .match-main { flex: 1 1 auto; }
  .board-stage { display: flex; flex-direction: column; height: 100%; padding: 6px; overflow: hidden; }
  .backgammon-board { flex: 1 1 auto; height: auto; min-height: 150px; }
  /* Checkers are sized from the viewport width by default; in a short landscape
     window they must follow the height instead or a five-stack overflows. */
  .checker { width: clamp(12px, 4.2vh, 26px); height: clamp(12px, 4.2vh, 26px); }
  .bar-stack .checker { width: clamp(11px, 3.6vh, 22px); height: clamp(11px, 3.6vh, 22px); }
  .stack-count { font-size: 8px; padding: 0 4px; }
  .cube { width: 26px; height: 26px; font-size: 11px; }

  /* .dice-area is a grid; the narrow-screen rules drop it to two columns and
     span the roll button across its own row. In landscape that row costs the
     board ~60px, so restore the single-row, three-column layout. */
  .dice-area { flex: 0 0 auto; grid-template-columns: 1fr auto auto; padding: 4px 6px; gap: 10px; }
  .roll-btn { grid-column: auto; flex: 0 0 auto; order: 0; min-height: 0; }
  .match-center em { white-space: nowrap; }
  .turn-copy small, .turn-copy span { display: none; }
  .turn-copy b { font-size: 13px; }
  .die { width: 34px; height: 34px; font-size: 14px; }
  .roll-btn { padding: 8px 18px; }

  .match-actions { flex: 0 0 auto; padding: 4px 6px; gap: 6px; }
  .match-actions button { min-height: 30px; padding: 4px 10px; font-size: 11px; }
}

@supports (height: 100dvh) { body { min-height: 100dvh; } }
.app-shell, .main, .content, .match-shell, .match-main, .board-stage,
.match-main > *, .match-shell > * { min-width: 0; }
.backgammon-board { max-width: 100%; }
.top-actions { min-width: 0; flex-wrap: wrap; justify-content: flex-end; }
.move-list, .chat-log, .decision-list, .leaderboard-table { overflow-x: auto; }

/* ---------------------------------------------------------------- phones */
@media (max-width: 760px) {
  .match-shell { gap: 10px; }
  /* Keep the top bar to one row: the essentials are the balance and the menu. */
  .topbar { height: auto; min-height: 64px; padding: 8px 12px; }
  .top-actions { gap: 6px; flex-wrap: nowrap; }
  .top-actions .btn.tiny { padding: 5px 9px; font-size: 11px; white-space: nowrap; }
  .credit-pill small { display: none; }
  .credit-pill { padding: 4px 8px; }
  #installAppBtn { display: none; }        /* the install banner covers this */
  .board-toolbar { flex-wrap: wrap; gap: 6px; }
  .toolbar-actions { margin-left: auto; }
  .toolbar-actions button { padding: 5px 8px; font-size: 11px; }
  .backgammon-board { min-height: 300px; height: 44vh; }
  .match-top { flex-wrap: wrap; gap: 8px; }
  .match-center strong { font-size: 22px; }
  .dice-area { gap: 10px; }
  .roll-btn { min-height: 48px; }
  .roll-btn kbd { display: none; }
  .match-actions { overflow-x: auto; scrollbar-width: none; }
  .match-actions::-webkit-scrollbar { display: none; }
  .pwa-banner { flex-wrap: wrap; }
  .pwa-banner-actions { margin-left: 0; width: 100%; justify-content: flex-end; }
}

/* Any short landscape viewport: keep the whole board on screen. */
@media (max-height: 560px) and (orientation: landscape) {
  .backgammon-board { height: min(70vh, 420px); min-height: 0; }
  .content { padding-bottom: 76px; }
  .dice-area { padding: 6px 10px; }
}

/* Landscape phones: the board is the point of the screen, so give it the room.
   The shell becomes a flex column and the board takes whatever is left, so the
   whole table fits without scrolling on a 360px-tall handset. */
@media (max-height: 520px) and (orientation: landscape) and (pointer: coarse) {
  .sidebar, .mobile-nav, .topbar, .match-side { display: none; }
  .app-shell { grid-template-columns: 1fr; }
  .content { padding: 6px; padding-bottom: 6px; }

  /* dvh is ideal (it excludes a collapsing address bar) but is not universal,
     so vh is the fallback — an invalid dvh value would drop the rule entirely. */
  .match-shell { display: flex; flex-direction: column; gap: 6px; height: calc(100vh - 12px); }
  .match-top { flex: 0 0 auto; padding: 4px 10px; }
  .match-center strong { font-size: 18px; }
  /* Grid and flex items default to min-height:auto, which makes them grow to
     fit content and ignore the height cap — the board must be allowed to shrink. */
  .match-main, .match-main > *, .board-stage { min-height: 0; }
  .match-main { flex: 1 1 auto; }
  .board-stage { display: flex; flex-direction: column; height: 100%; padding: 8px; overflow: hidden; }
  .board-toolbar, .dice-area, .match-actions { flex: 0 0 auto; }
  .backgammon-board { flex: 1 1 auto; height: auto; min-height: 0; }
  .dice-area { padding: 4px 8px; }
  .turn-copy small { display: none; }
}

@supports (height: 100dvh) {
  @media (max-height: 520px) and (orientation: landscape) and (pointer: coarse) {
    .match-shell { height: calc(100dvh - 12px); }
  }
}

/* ---------------------------------------------------------------- large screens */
@media (min-width: 1800px) {
  .app-shell { max-width: 1760px; margin: 0 auto; }
}

/* ---------------------------------------------------------------- accessibility */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Printing a position for study should not print the whole app chrome. */
@media print {
  .sidebar, .topbar, .mobile-nav, .match-side, .match-actions, .pwa-banner { display: none !important; }
  body { background: #fff; color: #000; }
}
