  /* Mobile / narrow-viewport layout. Loaded LAST so it overrides the desktop
     sidebar rules in panels.css/world.css. Everything is inside the media query,
     so on a wide desktop not a single rule applies → desktop is byte-for-byte
     unchanged.

     Engages on narrow viewports (<=820px) OR on a touch device up to tablet size
     (<=1024px AND a coarse pointer) — so phones, tablets, and landscape phones all
     get the drawer layout, while a desktop with a narrow window only switches below
     820px. THIS QUERY MUST STAY BYTE-IDENTICAL to mqMobile in app.js.

     The fix: the two fixed sidebars (#chatPanel left, #side right) leave the flex
     flow and become off-canvas drawers that slide over the world. #worldWrap
     (flex:1) is then the only in-flow child of #stage → the canvas is always
     full-width. Opening a drawer only animates `transform`, so it never changes
     the canvas size (no resizeCanvas needed on toggle). */
  @media (max-width: 820px), (max-width: 1024px) and (pointer: coarse) {
    /* #stage becomes the positioning context for the drawers + scrim. */
    #stage { position: relative; }

    #chatPanel, #side {
      position: absolute; top: 0; bottom: 0; z-index: 30;
      width: 86vw; max-width: 360px;
      transition: transform 180ms ease; will-change: transform;
    }
    /* Left chat/people drawer — reuse the existing cs-open/cs-collapsed state. */
    #chatPanel { left: 0; right: auto; transform: translateX(-100%); }
    #chatPanel.cs-open { transform: translateX(0); }
    /* On mobile the 40px rail is meaningless: collapsed == fully off-screen. */
    #chatPanel.cs-collapsed { width: 86vw; transform: translateX(-100%); }
    /* Right video/build drawer — toggled by .side-open (see app.js). */
    #side { right: 0; left: auto; transform: translateX(100%); }
    #side.side-open { transform: translateX(0); }

    /* Dim backdrop behind an open drawer; tap it to close (wired in app.js). */
    #drawerScrim {
      position: absolute; inset: 0; z-index: 25;
      background: rgba(4, 8, 16, 0.5); opacity: 0; pointer-events: none;
      transition: opacity 180ms ease;
    }
    #drawerScrim.show { opacity: 1; pointer-events: auto; }

    /* Drag-to-resize handles make no sense for fixed-width drawers. */
    #chatResizer, #sideResizer { display: none; }

    /* Toolbar: drop the wordmark + status text, trim the room tag, let the action
       row scroll horizontally rather than overflow a narrow screen. */
    #bar { gap: 6px; padding: 8px 10px; }
    #bar > b { display: none; }
    #status, #roomOwnerTag { display: none; }
    #roomTag { max-width: 30vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 3px 8px; }
    /* Wrap the action row onto a second line rather than scrolling it: a scroll
       container (overflow:auto) would clip the absolutely-positioned ⋯ dropdown,
       and wrapping also keeps every button visible (no off-screen leave button). */
    .bar-actions { gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
    .bar-actions .icon { flex: 0 0 auto; }
    /* Mobile-only: opens the right (video/build) drawer. Hidden on desktop (world.css). */
    #callBtn { display: inline-flex; }

    /* Bigger tap targets for touch. */
    .pbtn { width: 40px; height: 40px; font-size: 16px; border-radius: 9px; }
    .cs-toggle { padding: 8px 12px; font-size: 14px; }
    .lockbtn { padding: 6px 12px; font-size: 14px; }
    #reactBar button, #callCtl .icon { width: 42px; height: 42px; font-size: 22px; }
    /* On a phone the dock wraps to two rows — put reactions on top, controls below. */
    #reactBar { order: -1; }
    .vreactbar button { width: 38px; height: 38px; font-size: 18px; }

    /* More vertical room for the people list on a phone-tall drawer. */
    #peopleList { max-height: 40vh; }
  }
