/* ══════════════════════════════════════════════════════════════
   CC Web — Mobile Stylesheet
   ALL rules sit behind @media (max-width: 820px). Desktop layout
   must NOT change. If a mobile tweak escapes the breakpoint,
   tighten the breakpoint — never add a desktop override.

   Layered M2 rules cover: viewport, topbar, sidebar→drawer,
   right panel hide, dashboard reflow, modals→bottom-sheets,
   term-bar compact, login form. Tab strip and soft-keyboard
   toolbar arrive in M3/M4 (separate mobile.js).
   ══════════════════════════════════════════════════════════════ */

/* ── Hamburger button (drawer trigger) — RETIRED from the UI 2026-05-31.
       The 2K logo now toggles the hosts side menu (drawer) at EVERY viewport
       (desktop + mobile), so this standalone button is redundant everywhere.
       Kept in the DOM (mobile.js #mDrawerBtn handlers reference it safely) but
       hidden. Drawer access = 2K logo → body.drawer-open. */
.m-hamburger {
  display: none;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  margin-right: 6px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 6px;
  color: var(--text-high);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
}
.m-hamburger:active { background: var(--bg-surface); }
.m-hamburger:focus-visible { outline: 1px solid var(--neon-blue); outline-offset: 2px; }

/* ── Soft-keyboard toolbar + letter popover + jump-to-live — ALWAYS in
       DOM, hidden on desktop. Mobile @media block re-asserts the
       dimensions and adds body.m-kb-open .m-keybar { display: block }
       to reveal it when the OS keyboard pops up. Following the
       .m-hamburger always-in-DOM pattern so window-resize into the
       mobile breakpoint doesn't need lazy DOM injection. */
.m-keybar,
.m-letter-popover,
.m-jump-bottom,
.m-switch-pill { display: none; }

@media (max-width: 820px) {

  /* ──────────────────────────────────────────────────────────
     Body / viewport — dynamic height, no overscroll, scanline off
     ────────────────────────────────────────────────────────── */
  html, body {
    /* 100vh ignored where 100dvh is supported (later wins). 100dvh tracks
       the dynamic viewport, including browser-chrome auto-hide on scroll.
       Keyboard popup is handled separately by visualViewport JS in M4. */
    height: 100vh;
    height: 100dvh;
  }
  body {
    overscroll-behavior: contain;
    -webkit-tap-highlight-color: transparent;
  }
  /* Scanline overlay costs paint cycles on small high-DPI screens */
  body::after { display: none; }

  /* ──────────────────────────────────────────────────────────
     Topbar — compact, safe-area aware
     ────────────────────────────────────────────────────────── */
  .topbar {
    min-height: calc(44px + env(safe-area-inset-top));
    padding-top: env(safe-area-inset-top);
    padding-left: max(8px, env(safe-area-inset-left));
    padding-right: max(8px, env(safe-area-inset-right));
    gap: 6px;
  }

  /* Sprint 38: was flex:1 (logo ate the whole middle, .m-switch-pill had
     no room). Fixed-width now; the pill below takes flex:1.
     2026-05-31: bumped to a 44px WCAG touch target — 2K is the ONLY hosts-drawer
     trigger now (hamburger retired). padding:0 so the 44px box can't overflow the
     44px topbar row (box-sizing-agnostic); the 24px glyph stays centered. */
  .topbar-logo { min-width: 44px; min-height: 44px; padding: 0; justify-content: center; flex: 0 0 auto; gap: 8px; overflow: hidden; }
  .topbar-logo .logo-glyph { width: 24px; height: 24px; font-size: 9px; flex-shrink: 0; }

  /* Hide desktop tab strip — #topbarTabs stays the source of truth (desktop
     DOM, mirrored via MutationObserver in mobile.js). Sprint 38: session
     navigation moved from the bottom .m-tabbar strip to the topbar switcher
     pill below (always visible — keyboard never covers the topbar). */
  .topbar-tabs-wrapper { display: none; }

  /* ── Sprint 38 — Mobile session switcher pill ──────────────
     Lives in the topbar between the (now fixed-width) logo and
     .topbar-right; flex:1 takes the middle the logo released. Tap
     opens the existing fullscreen .m-tabgrid switcher (reused 1:1
     from Sprint M3). Replaces the bottom .m-tabbar strip, which
     body.m-kb-open hid while typing — the topbar never is.
     ────────────────────────────────────────────────────────── */
  .m-switch-pill {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 1;
    min-width: 0;
    height: 34px;
    padding: 0 10px;
    border-radius: 8px;
    background: var(--bg-raised);
    border: 1px solid var(--border-mid);
    color: var(--text-high);
    font-family: var(--mono);
    font-size: 12px;
    cursor: pointer;
    user-select: none;
    -webkit-tap-highlight-color: rgba(var(--neon-blue-rgb), 0.18);
  }
  .m-switch-pill:active { background: var(--bg-active); }
  .m-switch-pill.has-active {
    border-color: var(--neon-blue);
    box-shadow: 0 0 0 1px rgba(var(--neon-blue-rgb), 0.35), var(--glow-blue);
  }
  .m-switch-pill.empty { color: var(--text-low); border-style: dashed; }
  /* Dot reuses the desktop .dot status classes (copied className, same LEGO
     pattern the old strip used at syncTabStrip) so the pill's status colour
     matches the active tab. Only sizing is set here. */
  .m-switch-pill .dot { width: 8px; height: 8px; flex-shrink: 0; }
  .m-switch-pill .msp-label {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .m-switch-pill .msp-count {
    flex-shrink: 0;
    background: rgba(var(--neon-blue-rgb), 0.18);
    color: var(--neon-blue);
    border-radius: 10px;
    padding: 2px 7px;
    font-size: 10px;
    font-weight: 700;
  }
  .m-switch-pill .msp-chev { flex-shrink: 0; color: var(--text-low); font-size: 10px; }

  /* Sprint M4f (2026-05-09): the standalone .topbar-tile-btn /
     .topbar-prompt-btn rule was removed. Sprint 24 collapsed those into
     the menu dropdown — the same classes now identify the DROPDOWN items
     (e.g. <button class="topbar-action-btn topbar-tile-btn">Tile mode</button>).
     A flat `display: none` matched both the (no-longer-existing) standalone
     buttons AND the dropdown items, hiding Tile/Prompt rows from the menu.
     M4f scoped hiding (below in this @media block) targets the dropdown
     items by ID + does NOT hit Prompt Hub which is mobile-friendly. */

  /* Online pulse stays visible — useful, small footprint */
  .topbar-pulse { font-size: 9px; flex-shrink: 0; }
  .topbar-pulse #onlineCount { font-size: 10px; }

  /* Compact-icon mode for the topbar trigger buttons (hamburger ⋮ menu, etc.).
     Sprint M4f (2026-05-09): scoped narrowly to direct topbar children +
     menu-wrap > button so labels INSIDE .topbar-menu-dropdown remain visible.
     The previous broad selector `.topbar-action-btn .lbl { display: none }`
     hid every label including the dropdown items, leaving the menu as an
     unusable column of bare icons on mobile. */
  .topbar > .topbar-right > .topbar-pulse .lbl,
  .topbar > .topbar-right > .topbar-action-btn .lbl,
  .topbar-menu-wrap > .topbar-action-btn .lbl,
  .topbar > .topbar-action-btn .lbl { display: none; }
  .topbar > .topbar-right > .topbar-action-btn,
  .topbar-menu-wrap > .topbar-action-btn,
  .topbar > .topbar-action-btn { padding: 0 8px; min-width: 36px; }
  .topbar > .topbar-right > .topbar-action-btn .ico,
  .topbar-menu-wrap > .topbar-action-btn .ico,
  .topbar > .topbar-action-btn .ico { font-size: 16px; }

  /* Menu dropdown — anchor to right edge, clamp width */
  .topbar-menu-dropdown {
    right: 0 !important;
    left: auto !important;
    width: min(280px, 92vw);
    max-height: calc(100dvh - 80px);
    overflow-y: auto;
  }
  .topbar-menu-dropdown .hint { display: none; } /* hide kbd hints on touch */
  /* Dropdown items: re-assert label sizing so M4f scope-fix flows through
     cleanly. Dropdown items are buttons + flex-aligned by layout.css; we
     only need to enforce font + truncation at the mobile breakpoint. */
  .topbar-menu-dropdown > .topbar-action-btn .lbl {
    font-size: 13px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* Hide items that don't make sense on mobile:
     - Tile mode: reconcileModes auto-disables it on mobile (mobile.js:84) — Sprint 28 promoted
       the button out of the dropdown into .topbar-right, so the selector targets that location.
     - Vertical tabs: toggleVerticalTabs early-returns on mobile (app.js:1380)
     - Quick terminal: .float-term display:none on mobile (mobile.css)
     - Activity log: opens .rpanel which is display:none on mobile (mobile.css)
     Keep them in DOM (event handlers stay wired); CSS just hides the element. */
  .topbar > .topbar-right > #topbarTileBtn,
  .topbar-menu-dropdown #topbarVtabsBtn,
  .topbar-menu-dropdown #topbarFloatBtn,
  .topbar-menu-dropdown #btnActivity { display: none; }
  /* Theme item: open/close a flyout via tap (CSS :hover doesn't work on touch).
     The .open class is toggled by app.js when the row is tapped. Keep the
     existing :hover rule so desktop UX is unchanged. */
  .topbar-theme-wrap.open .theme-submenu { display: flex !important; }
  /* On mobile, flyout drops INSIDE the dropdown (right:100% would put it
     off-screen against the right-anchored dropdown). Center under the row. */
  .topbar-menu-dropdown .theme-submenu {
    position: static;
    margin: 4px 0 0;
    width: 100%;
    min-width: 0;
    box-shadow: none;
    border: 1px solid var(--border-dim);
    background: var(--bg-deepest);
  }

  /* ──────────────────────────────────────────────────────────
     Sidebar drawer — mobile-specific size + safe-area overrides.
     Core drawer mechanics (position:fixed, transform, transition,
     z-index, box-shadow, backdrop, hide resizer/toggle) are now in
     layout.css as the cross-viewport default (Sprint 19, 2026-05-09).
     Mobile keeps only what genuinely differs: full-width slide-out
     and safe-area-inset top so the drawer clears the notch.
     ────────────────────────────────────────────────────────── */
  .sidebar {
    top: calc(var(--topbar-height) + env(safe-area-inset-top));
    width: 84vw;
    max-width: 360px;
  }
  body.drawer-open::before {
    top: calc(var(--topbar-height) + env(safe-area-inset-top));
  }

  /* Larger hit targets for touch */
  .s-item {
    padding: 12px 14px;
    font-size: 14px;
    min-height: 48px;
    align-items: center;
  }
  .s-action {
    height: 30px;
    min-width: 36px;
    padding: 0 10px;
    font-size: 11px;
  }

  /* Sidebar footer — slim on mobile */
  .sidebar-footer { padding: 10px 14px; }
  .sf-sparkline-row { display: none; }

  /* ──────────────────────────────────────────────────────────
     Right panel — fully hidden on mobile (files & activity)
     ────────────────────────────────────────────────────────── */
  .rpanel { display: none !important; }

  /* ──────────────────────────────────────────────────────────
     Content area — Sprint 38: no bottom strip to reserve anymore
     (session pill moved to the topbar). Only the safe-area inset
     remains. Terminal height is JS-driven (--mobile-term-height).
     ────────────────────────────────────────────────────────── */
  .content {
    width: 100%;
    padding-bottom: env(safe-area-inset-bottom);
  }

  /* ──────────────────────────────────────────────────────────
     Dashboard reflow
     ────────────────────────────────────────────────────────── */
  .dash { padding: 14px 12px; }
  .dash-greeting { margin-bottom: 16px; }
  .dash-greeting h1 { font-size: 18px; }
  .dash-greeting .dg-pre { font-size: 9px; }
  .dash-greeting .dg-sub { font-size: 11px; }

  .kpis {
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    margin-bottom: 16px;
  }
  .kpi { padding: 12px 14px; }
  .kpi .kpi-num { font-size: 22px; }
  .kpi .kpi-label { font-size: 9px; }
  .kpi .kpi-detail { font-size: 9px; }

  /* Active sessions — single column stack */
  .asw-list { flex-direction: column; gap: 8px; }
  .asw-card { min-width: 0; max-width: none; flex: 1 1 auto; }

  /* Quick launch + hosts grid → single column */
  .actions { grid-template-columns: 1fr; gap: 8px; margin-bottom: 18px; }
  .hosts-grid { grid-template-columns: 1fr; gap: 8px; }

  /* Recent sessions list — 100% width, slimmer rows */
  .audit-list .audit-row { padding: 10px 12px; }

  /* ──────────────────────────────────────────────────────────
     Modals → bottom sheets (palette, prompt-hub, batch, editor, cred)
     ────────────────────────────────────────────────────────── */
  .modal-bg,
  .palette-bg,
  .prompt-hub-overlay {
    align-items: flex-end !important;
    padding: 0 !important;
  }
  .modal,
  .palette,
  .prompt-hub-modal,
  .editor-modal,
  .batch-modal {
    width: 100% !important;
    max-width: none !important;
    max-height: 86dvh;
    border-radius: 14px 14px 0 0 !important;
    margin: 0 !important;
    padding: 18px 16px !important;
    animation: m-sheet-up 0.25s ease;
  }
  @keyframes m-sheet-up {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
  }
  /* Sheet handle for visual affordance */
  .modal::before,
  .palette::before,
  .prompt-hub-modal::before,
  .editor-modal::before,
  .batch-modal::before {
    content: '';
    display: block;
    width: 36px;
    height: 4px;
    background: var(--border-bright);
    border-radius: 2px;
    margin: -6px auto 12px;
    opacity: 0.6;
  }

  .palette input { font-size: 16px; height: 44px; }
  .palette-list { max-height: 60dvh; }

  .editor-content { font-size: 13px; min-height: 40dvh; }

  /* Floating quick terminal hidden — every term is fullscreen on mobile */
  .float-term { display: none !important; }

  /* ──────────────────────────────────────────────────────────
     Term view + term bar (compact toolbar)
     ────────────────────────────────────────────────────────── */
  /* .term-view fills .content; .content already reserves space for the
     mobile tab strip + safe-area, so no extra term-view padding needed
     (would double-count and clip the terminal). */
  .term-bar {
    height: 36px;
    padding: 0 8px;
    gap: 6px;
    overflow: hidden;
    flex-wrap: nowrap;
  }
  /* Hide desktop-only toolbar buttons. Note: split-h, split-v, rec-btn no
     longer exist as inline buttons (Sprint 26 collapsed them into the
     tb-more-wrap dropdown), so those selectors are no-ops kept for older
     cached HTML compatibility. tb-more-wrap is hidden too — mobile has its
     own M4b soft-keyboard toolbar with split/rec/scroll affordances. */
  .term-bar .tb-panes,
  .term-bar .tb-btn.split-h,
  .term-bar .tb-btn.split-v,
  .term-bar .tb-btn.pane-zoom,
  .term-bar .tb-btn.pane-close,
  .term-bar .tb-btn.rec-btn,
  .term-bar .tb-btn.upload-btn,
  .term-bar .tb-btn.popout-btn,
  .term-bar .tb-btn.ide-btn,
  .term-bar .tb-btn.files-toggle,
  .term-bar .tb-btn.force-redraw,
  .term-bar .tb-more-wrap,
  .term-bar .tb-broadcast { display: none !important; }

  /* Keep visible: status, host name, font ±, scroll-bottom, kill */
  .term-bar .tb-btn.font-minus,
  .term-bar .tb-btn.font-plus,
  .term-bar .tb-btn.scroll-bottom,
  .term-bar .tb-btn.kill {
    min-width: 36px;
    height: 28px;
    padding: 0 8px;
    font-size: 11px;
  }
  .term-bar .tb-host { font-size: 11px; max-width: 35vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .term-bar .tb-status { font-size: 10px; }
  .term-bar .tb-sep { display: none; }

  /* Term container — viewport-aware height (set by mobile.js in M4 from
     visualViewport.height; falls back to dvh when keyboard is closed).
     Defined once below in the M3 block where the 52px tab-strip is also
     accounted for. */

  /* ──────────────────────────────────────────────────────────
     Settings view — single column forms
     ────────────────────────────────────────────────────────── */
  .settings-view { padding: 14px 12px; }
  .settings-section { padding: 14px 12px; }
  /* .form-row is display:flex (components.css:269) — stack columns on mobile */
  .form-row { flex-direction: column; gap: 10px; }
  .form-group input,
  .form-group select,
  .form-group textarea {
    height: 44px;
    font-size: 16px; /* prevents iOS Safari auto-zoom on focus */
  }
  .settings-section input[type="text"],
  .settings-section input[type="password"] { font-size: 16px; }

  /* ──────────────────────────────────────────────────────────
     Login form — fits a phone, no auto-zoom
     ────────────────────────────────────────────────────────── */
  .login-container { padding: 12px; }
  .login-box {
    width: 92vw;
    max-width: 360px;
    padding: 24px 18px;
  }
  .login-box h2 { font-size: 18px; }
  .login-box input[type="password"] {
    height: 48px;
    font-size: 16px; /* iOS Safari no-zoom min */
  }
  .login-btn { height: 46px; font-size: 14px; }

  /* ──────────────────────────────────────────────────────────
     Touch affordances on tappable surfaces (.topbar-tab is hidden,
     so omitted)
     ────────────────────────────────────────────────────────── */
  .s-item,
  .kpi,
  .action,
  .asw-card { -webkit-tap-highlight-color: rgba(var(--neon-blue-rgb),0.18); }
  .kpi:hover { transform: none; }

  /* ──────────────────────────────────────────────────────────
     Sprint 38 — the bottom tab strip (.m-tabbar / .m-tabstrip /
     .m-tab / .m-tab-grid-btn, ~90 lines) was REMOVED here.
     Session navigation moved to the always-visible .m-switch-pill
     in the topbar (rules above, near .topbar-tabs-wrapper). The
     fullscreen .m-tabgrid switcher is unchanged and now opened by
     the pill instead of the strip's ⊞ button. Removal frees 52px
     of vertical space back to the terminal. Old rules recoverable
     via mobile.css.pre-mobile-pill-2026-05-19 or git 887acbd.
     ────────────────────────────────────────────────────────── */

  /* Term container — viewport-aware height, accounts for topbar (44),
     term-bar (36), and both safe-area insets. Sprint 38: tab strip (52)
     dropped from the fallback — strip removed, pill is in the topbar.
     mobile.js overrides --mobile-term-height when the keyboard pops up.
     touch-action: pan-y disables browser-level pinch-zoom on the
     terminal so 2-finger gestures route to our pinch-to-zoom-font
     handler (M4d). page-level pinch still works elsewhere via viewport
     meta user-scalable=yes. */
  .term-container {
    height: var(--mobile-term-height, calc(100dvh - 44px - 36px - env(safe-area-inset-top) - env(safe-area-inset-bottom)));
    touch-action: pan-y;
  }
  /* Native touch scroll on the xterm scrollback. .xterm-viewport already
     has overflow-y:scroll from the addon, but mobile browsers won't pan
     it without explicit touch-action + -webkit-overflow-scrolling. The
     JS handler in mobile.js runs in capture phase as a fallback when
     native scroll falls through (e.g. tap-and-drag in TUI mouse mode). */
  .term-container .xterm-viewport {
    -webkit-overflow-scrolling: touch;
    touch-action: pan-y;
    overscroll-behavior: contain;
  }

  /* ──────────────────────────────────────────────────────────
     Fullscreen tab grid switcher (Chrome-style)
     ────────────────────────────────────────────────────────── */
  .m-tabgrid {
    position: fixed;
    inset: 0;
    z-index: 250; /* above content, drawer, palette (100); below prompt-hub (300) */
    background: var(--bg-void);
    display: none;
    flex-direction: column;
    padding-top: env(safe-area-inset-top);
    padding-bottom: env(safe-area-inset-bottom);
  }
  .m-tabgrid.visible {
    display: flex;
    animation: m-grid-up 0.18s ease;
  }
  @keyframes m-grid-up {
    from { opacity: 0; transform: scale(0.96); }
    to   { opacity: 1; transform: scale(1); }
  }
  .m-tabgrid-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 14px;
    border-bottom: 1px solid var(--border-dim);
    font-family: var(--mono);
    font-size: 12px;
    color: var(--text-mid);
  }
  .m-tabgrid-close {
    background: transparent;
    border: none;
    color: var(--text-high);
    font-size: 22px;
    width: 36px;
    height: 36px;
    cursor: pointer;
  }
  .m-tabgrid-list {
    flex: 1;
    overflow-y: auto;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
    padding: 12px;
    align-content: start;
  }
  .m-grid-card {
    aspect-ratio: 4 / 3;
    background: var(--bg-surface);
    border: 1px solid var(--border-dim);
    border-radius: 10px;
    overflow: hidden;
    position: relative;
    display: flex;
    flex-direction: column;
    cursor: pointer;
  }
  .m-grid-card.active { border-color: var(--neon-blue); box-shadow: var(--glow-blue); }
  .m-grid-card-head {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 8px 10px;
    border-bottom: 1px solid var(--border-dim);
    font-size: 11px;
    color: var(--text-high);
  }
  .m-grid-card-head .dot { width: 7px; height: 7px; flex-shrink: 0; }
  .m-grid-card-head .m-grid-card-name {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .m-grid-card-x {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    color: var(--text-low);
    font-size: 14px;
    line-height: 1;
    flex-shrink: 0;
  }
  .m-grid-card-x:active { color: var(--neon-red); background: rgba(var(--neon-red-rgb),0.12); }
  .m-grid-card-preview {
    flex: 1;
    padding: 8px 10px;
    font-family: var(--mono);
    font-size: 9px;
    line-height: 1.35;
    color: var(--text-mid);
    overflow: hidden;
    white-space: pre-wrap;
    word-break: break-all;
  }
  .m-tabgrid-foot {
    padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
    border-top: 1px solid var(--border-dim);
  }
  .m-tabgrid-new {
    width: 100%;
    height: 44px;
    background: rgba(var(--neon-green-rgb),0.08);
    border: 1px solid rgba(var(--neon-green-rgb),0.25);
    border-radius: 8px;
    color: var(--neon-green);
    font-family: var(--mono);
    font-size: 13px;
    cursor: pointer;
  }
  .m-tabgrid-new:active { background: rgba(var(--neon-green-rgb),0.14); }
  .m-tabgrid-empty {
    grid-column: 1 / -1;
    text-align: center;
    color: var(--text-low);
    font-family: var(--mono);
    font-size: 11px;
    padding: 40px 0;
  }

  /* ──────────────────────────────────────────────────────────
     Soft-keyboard toolbar (m-keybar) — Sprint M4b
     Floats above the OS virtual keyboard. Visibility piggy-backs
     on body.m-kb-open (set by attachVisualViewport when kbHeight
     crosses the 100px threshold OR when xterm.textarea has focus).
     ────────────────────────────────────────────────────────── */
  .m-keybar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 150; /* above content (90), below modals (200+) */
    background: var(--bg-deep);
    border-top: 1px solid var(--border-mid);
    padding: 6px 8px;
    padding-bottom: max(6px, env(safe-area-inset-bottom));
    display: none;
  }
  body.m-kb-open .m-keybar { display: block; }

  .m-keybar-row {
    display: flex;
    gap: 4px;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .m-keybar-row::-webkit-scrollbar { display: none; }

  .m-keybar button {
    flex: 0 0 auto;
    min-width: 44px;
    height: 36px;
    padding: 0 10px;
    font-family: var(--mono);
    font-size: 13px;
    background: var(--bg-surface);
    border: 1px solid var(--border-dim);
    border-radius: 6px;
    color: var(--text-high);
    cursor: pointer;
    user-select: none;
    -webkit-user-select: none;
    -webkit-tap-highlight-color: transparent;
    /* manipulation = no double-tap zoom + faster click on mobile */
    touch-action: manipulation;
  }
  .m-keybar button:active { background: var(--bg-active); }
  .m-keybar button.m-mod.armed {
    background: var(--neon-blue);
    border-color: var(--neon-blue);
    color: var(--bg-deepest);
  }
  .m-keybar button.m-mod.locked {
    background: var(--neon-amber);
    border-color: var(--neon-amber);
    color: var(--bg-deepest);
    box-shadow: var(--glow-amber);
  }
  .m-keybar button.m-key-yes {
    background: rgba(var(--neon-green-rgb), 0.15);
    color: var(--neon-green);
    border-color: var(--neon-green);
  }
  .m-keybar button.m-key-no {
    background: rgba(var(--neon-red-rgb), 0.15);
    color: var(--neon-red);
    border-color: var(--neon-red);
  }
  .m-keybar button.m-action {
    background: var(--bg-raised);
    color: var(--text-max);
  }

  /* Letter popover — Tier 2 modifier path for iOS Safari, where
     keydown events on xterm.textarea are unreliable for letter keys.
     Shown only when an armed Ctrl/Alt modifier is tapped on iOS or
     when the user explicitly forces the popover via localStorage. */
  .m-letter-popover {
    position: fixed;
    left: 8px;
    right: 8px;
    z-index: 160; /* above .m-keybar (150) */
    background: var(--bg-deepest);
    border: 1px solid var(--border-bright);
    border-radius: 10px;
    padding: 12px;
    box-shadow: 0 -8px 24px rgba(0,0,0,0.6);
    display: none;
  }
  .m-letter-popover.visible { display: block; }
  .m-letter-popover-title {
    font-family: var(--mono);
    font-size: 11px;
    color: var(--text-mid);
    margin-bottom: 8px;
    text-align: center;
  }
  .m-letter-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 6px;
  }
  .m-letter-grid button {
    height: 40px;
    font-family: var(--mono);
    font-size: 14px;
    background: var(--bg-surface);
    border: 1px solid var(--border-dim);
    border-radius: 6px;
    color: var(--text-high);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
  }
  .m-letter-grid button:active { background: var(--bg-active); }
  .m-letter-popover-cancel {
    width: 100%;
    margin-top: 10px;
    height: 36px;
    background: transparent;
    border: 1px solid var(--border-dim);
    border-radius: 6px;
    color: var(--text-mid);
    font-size: 12px;
    cursor: pointer;
  }

  /* ──────────────────────────────────────────────────────────
     Jump-to-live floating button (Sprint M4e — spec §7.4)
     Shown when the active terminal's scrollback drifts more
     than 2 rows above the live tail. Tap → scrollToBottom.
     z-index 155: above content (90) + keybar (150); below letter
     popover (160) and modals (200+) so popover and sheets can
     still cover it cleanly. (Sprint 38: m-tabbar removed.)
     ────────────────────────────────────────────────────────── */
  .m-jump-bottom {
    position: fixed;
    right: 14px;
    /* Default position — Sprint 38: no tab strip beneath it anymore,
       so it sits just above the safe-area bottom when the OS keyboard
       is closed. body.m-kb-open below shifts it up above the keybar. */
    bottom: calc(env(safe-area-inset-bottom) + 14px);
    z-index: 155;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: var(--neon-blue);
    border: 1px solid var(--neon-blue);
    color: var(--bg-deepest);
    font-family: var(--mono);
    font-size: 22px;
    line-height: 1;
    cursor: pointer;
    box-shadow: var(--glow-blue), 0 4px 12px rgba(0,0,0,0.4);
    /* Hidden by default — show only when .visible is added by
       updateJumpToLiveVisibility(). pointer-events: none keeps a
       hidden button untappable through the opacity:0 cloak. */
    opacity: 0;
    pointer-events: none;
    transform: scale(0.7);
    transition: opacity 0.18s ease, transform 0.18s ease;
    -webkit-tap-highlight-color: transparent;
    /* manipulation = no double-tap zoom + faster click on mobile */
    touch-action: manipulation;
  }
  .m-jump-bottom.visible {
    opacity: 0.95;
    pointer-events: auto;
    transform: scale(1);
  }
  /* :active needs higher specificity than .visible's transform —
     compound selector wins, so press feedback overrides idle scale. */
  .m-jump-bottom.visible:active {
    transform: scale(0.92);
    opacity: 1;
  }
  /* Keyboard open — m-keybar (≈48px + safe-area) is at the bottom.
     Sit above the keybar so the user can still tap ↓ while typing
     into a Claude prompt. */
  body.m-kb-open .m-jump-bottom {
    bottom: calc(48px + max(6px, env(safe-area-inset-bottom)) + 12px);
  }

  /* ──────────────────────────────────────────────────────────
     Host menu — bottom-sheet on mobile (Sprint M4f, 2026-05-09)
     The desktop dropdown is anchored to the right of the .s-item
     via host-menu.js _positionMenu. On mobile (84vw drawer + small
     viewport) that anchor falls off-screen and the menu got tiny
     11-px font + ~28-px tap targets with descriptions clipping at
     the right edge. M4f: host-menu.js skips JS positioning on
     mobile; CSS takes over with a full-width slide-up sheet.
     `!important` overrides any inline left/top JS may still write
     during the brief setTimeout-0 race before the early-return
     applies (defensive — _positionMenu is the single writer, but
     belt-and-suspenders here costs nothing).
     ────────────────────────────────────────────────────────── */
  .host-menu {
    position: fixed !important;
    left: 0 !important;
    right: 0 !important;
    top: auto !important;
    bottom: 0 !important;
    width: 100% !important;
    max-width: none !important;
    max-height: 80dvh;
    border-radius: 14px 14px 0 0;
    padding: 8px 0 calc(8px + env(safe-area-inset-bottom));
    box-shadow: 0 -8px 32px rgba(var(--bg-void-rgb), 0.7);
    /* No `transform` override here — _positionMenu early-returns on mobile so
       no inline transform is set, and `!important` would block the m-sheet-up
       animation keyframes (CSS Cascade L4 §6.4.4: !important beats animation
       declarations). The base .host-menu has no transform. */
    animation: m-sheet-up 0.25s ease;
    z-index: 500; /* above drawer (80), tabbar (90), keybar (150), popover (160) */
  }
  /* Sheet handle (visual affordance for swipe-down dismissal — tap-out
     dismissal still works via the existing outsideClickHandler). */
  .host-menu::before {
    content: '';
    display: block;
    width: 36px;
    height: 4px;
    background: var(--border-bright);
    border-radius: 2px;
    margin: 2px auto 8px;
    opacity: 0.6;
  }
  .host-menu-header { padding: 8px 16px 12px; }
  .host-menu-title { font-size: 14px; }
  .host-menu-meta { font-size: 10px; }
  .host-menu-section {
    font-size: 10px;
    padding: 10px 16px 4px;
    letter-spacing: 1.5px;
  }
  .host-menu-item {
    padding: 12px 16px;
    min-height: 48px; /* WCAG/iOS-HIG 44-px tap target with breathing room */
    font-size: 13px;
    -webkit-tap-highlight-color: rgba(var(--neon-blue-rgb), 0.18);
  }
  .host-menu-item .hm-icon {
    font-size: 16px;
    width: 22px;
  }
  .host-menu-item .hm-label { font-size: 13px; }
  .host-menu-item .hm-desc {
    font-size: 10px;
    max-width: 110px;
  }
  /* Dim backdrop behind the host-menu sheet. The host-menu module
     doesn't insert a backdrop element of its own (it's a desktop-style
     dropdown). On mobile we want a clear "modal-ish" feel — pseudo-
     element on body when a .host-menu is in the DOM. */
  body:has(.host-menu)::after {
    content: '';
    position: fixed;
    inset: 0;
    background: rgba(var(--bg-void-rgb), 0.55);
    backdrop-filter: blur(2px);
    z-index: 499; /* one below .host-menu */
    pointer-events: none; /* outside-click handler in host-menu.js still fires */
  }
}
