  /* =========================================================================
     responsive.css — mobile / narrow-viewport layer (P0).
     Loaded LAST in index.html so these equal-specificity rules win the cascade
     over the per-component sheets. Additive only: no desktop rule above the
     breakpoints is changed, so behaviour ≥641px is identical to before.
     Breakpoints: 640px (shell rail → top bar), 560px (tile grids / key-value).
     ========================================================================= */

  /* Modals never touch the screen edge on narrow viewports. min() leaves the
     480px desktop cap intact (viewport ≥504px) and adds 12px side gutters below
     that. Intentionally-wide modals use a more specific selector and still win.
     .fs-picker is NOT a .modal (own .fs-picker-back), so it is unaffected. */
  .modal { max-width: min(480px, calc(100vw - 24px)); }

  @media (max-width: 640px) {
    /* The 156px sidebar rail collapses into a sticky top header: logo on its
       own line (top-left), then the menu buttons as one full-width row. */
    /* APP-SHELL: на мобиле портал — это фиксированный кадр в 100dvh, БЕЗ общей
       прокрутки body. Скроллится только контент (.main) и собственные панели.
       Так контент не «вылезает» за экран целиком — он помещается, а длинное
       листается ВНУТРИ .main. (Не скрываем скролл — его просто нет на body.) */
    html, body { height: 100%; overflow: hidden; }
    .app {
      grid-template-columns: 1fr;
      grid-template-rows: auto 1fr;   /* строка1 = top-bar (.side), строка2 = .main */
      height: 100dvh; min-height: 0;
    }
    /* .main — единственный вертикальный скролл-контейнер на мобиле. */
    .main {
      overflow-y: auto; overflow-x: hidden;
      -webkit-overflow-scrolling: touch;
      min-height: 0;
    }
    /* The 156px sidebar rail collapses into a sticky top header (flex column):
       row 1 = logo + plus balance (.brand-row), row 2 = the full-width nav. */
    .side {
      position: sticky; top: 0; z-index: 60;
      width: 100%; height: auto;
      flex-direction: column; align-items: stretch; gap: 8px;
      /* Respect the notch / status bar in standalone PWA mode (env() is 0 on
         non-notched devices, so max() keeps the normal 8/12px otherwise). */
      padding: max(8px, env(safe-area-inset-top)) max(12px, env(safe-area-inset-right))
               8px max(12px, env(safe-area-inset-left));
      border-right: 0; border-bottom: 1px solid var(--border);
    }
    /* Logo (left) + plus balance (right) share one row. .brand-row is
       display:contents on desktop (see shell.css), so this only kicks in here. */
    .side .brand-row {
      display: flex; align-items: center; justify-content: space-between;
      gap: 10px; width: 100%;
    }
    .side .brand-row .brand { padding: 0; margin: 0; }
    .side .brand-row .brand-balance { margin: 0; flex: 0 0 auto; }
    /* nav is the full-width second row. Equal 1fr columns keep buttons as neat
       squares regardless of count (admin/moderator adds one). */
    .side .nav {
      grid-column: 1 / -1;
      display: grid;
      grid-template-columns: none;
      grid-auto-flow: column;
      grid-auto-columns: 1fr;
      gap: 6px;
      width: 100%;
    }
    /* Compact top bar: square icon-only buttons, icon DEAD-CENTERED. Flex
       centering is bulletproof — no residual padding / hidden-label offset. */
    .side .nav .icon-btn {
      aspect-ratio: 1 / 1;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 0;
      padding: 0;
    }
    .side .nav .icon-btn > svg { margin: 0; display: block; }
    .side .nav-label { display: none; }
    .side .side-toggle { display: none; }
    /* The anon-nudge block does not fit the compact bar — hidden on mobile;
       the same prompt still lives in Settings → Profile. */
    .side .anon-nudge-badge { display: none; }
    /* Reclaim horizontal space lost to the desktop 36px gutters; add the
       bottom/side safe-area insets (home indicator, landscape notch). */
    .main {
      padding: 16px max(14px, env(safe-area-inset-right))
               calc(48px + env(safe-area-inset-bottom)) max(14px, env(safe-area-inset-left));
    }
  }

  /* Tablet: sidebar goes icon-only (narrower) at 768px. */
  @media (max-width: 768px) {
    .side .nav-label { display: none; }
    .side .anon-nudge-badge { display: none; }
    .app.nav-collapsed .side .nav,
    .side .nav {
      grid-template-columns: none;
      grid-auto-flow: column;
      grid-auto-columns: 1fr;
    }
    .side .nav .icon-btn {
      aspect-ratio: 1 / 1;
      display: grid;
      place-items: center;
      gap: 0;
      padding: 0;
    }
    .side .side-toggle { display: none; }
  }

  @media (max-width: 560px) {
    /* Tile grids: 240/280px min-cells overflow a 360px screen → shrink so two
       tiles fit side by side. Also helps the .my-grid fullscreen ad picker. */
    .grid-games, .my-grid {
      grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
      gap: 12px;
    }
    /* Catalog: one game per row on narrow screens (my-games stays 2-up). */
    .my-grid[data-cat-grid] { grid-template-columns: 1fr; }
    /* Game-detail key/value: the fixed 200px label column collapses to stacked
       rows so values stop getting crushed on a phone. */
    .kv { grid-template-columns: 1fr; gap: 2px 0; }
    .kv .k { margin-top: 6px; }

    /* "New game" create row: 180px paste/drop tiles + the 240px engine tile sit
       in a flex row that overflows a phone — wrap and let each go full width. */
    .paste-screen-top { flex-wrap: wrap; gap: 12px; min-height: 0; }
    .huge-paste, .drop-zone { width: 100%; height: 140px; }
    .game-new-engine-tile { width: 100%; min-width: 0; max-width: none; height: auto; min-height: 96px; }
  }

  /* =========================================================================
     Touch devices (no real hover). Fixes two P1 problems:
       1) Tile/card controls are opacity:0 until :hover on desktop, so on touch
          they are invisible/unreachable — reveal them permanently.
       2) A tap can leave the desktop :hover lift (translateY) stuck — neutralize
          it. Also bump the main tile controls to a 44px touch target.
     (trash stays gated behind body.ctrl-on — an intentional desktop power mode.)
     ========================================================================= */
  @media (hover: none) {
    .my-tile .frame .play-fs,
    .my-tile .frame .tile-admin-btn,
    .my-tile .frame .game-act-btn,
    .game-card .game-act-btn,
    .inv-card .sell-btn { opacity: 1; }

    .my-tile:hover, .game-card:hover, .inv-card:hover { transform: none; }

    .my-tile .frame .play-fs,
    .my-tile .frame .tile-admin-btn,
    .my-tile .frame .game-act-btn,
    .game-card .game-act-btn { width: 44px; height: 44px; }
  }

  /* Narrow phones: single-column game grid. */
  @media (max-width: 380px) {
    .grid-games { grid-template-columns: 1fr; }
  }

  /* P0 MUST stay last — iOS Safari zoom fix: inputs at <16px trigger auto-zoom.
     Placed at the end of the cascade so no per-component rule can override it. */
  @media (max-width: 768px) {
    input, textarea, select { font-size: 16px; }
  }
