/* OS/A Relay — the phone.
   ═══════════════════════════════════════════════════════════════════════════

   Loaded after relay.css, and everything in it lives inside one media query.
   Below the breakpoint the layout changes KIND rather than degree: the sidebar
   leaves the flow and becomes a drawer, the conversation takes the whole
   screen, split view is off, and the workspace switcher is the space rail laid
   on its side. relay.css used to answer this width by narrowing its grid to
   `220px 1fr`, which on a 390px screen leaves 170px of conversation. That
   block has been removed; nothing narrower than a desktop is decided there.

   ── What this file will not do ──

   It never forks the interface. There is no mobile-only sidebar, no second
   conversation list, no parallel render path. Every control here drives the
   function the desktop control drives, and the space switcher IS the desktop
   rail — `renderRail()` output, laid out in a row instead of a column. A
   second implementation of the sidebar would look like progress and pass its
   own tests, and the day somebody fixed a bug in one of them the phone would
   keep the bug.

   ── The breakpoint is declared here, once ──

   `--mobile` below is how mobile.js knows. It reads the custom property off
   the root element rather than holding a second copy of `720px`, so the CSS
   and the JavaScript cannot disagree about which layout is on screen. If you
   move the breakpoint, move it here and nowhere else. */

@media (max-width: 720px) {

  /* The one fact mobile.js asks for. See the note above before adding a
     second media query to this file — `Relay.mobile.on()` reads this
     property, and a rule outside this block is a rule the JavaScript cannot
     see. */
  :root { --mobile: 1; }

  /* ══ Frame ══ */

  /* `100vh` on iOS Safari means "the window with the URL bar not counted",
     so the composer sits underneath it and the send button cannot be
     pressed. `dvh` is the visible height and it tracks the bar retracting. */
  #app {
    display: block;
    /* `--vvh` is the VISUAL viewport's height, published by mobile.js while
       the keyboard is up. `100dvh` is the fallback and is not enough on its
       own: dvh accounts for the URL bar retracting and says nothing about a
       keyboard, so with one open the app stayed full height, the browser
       scrolled the page to reveal the composer, and everything above it —
       the bar, the member chips, the message being answered — went off the
       top. What you got was a composer floating in a blank screen. */
    height: var(--vvh, 100dvh);
    /* The grid is gone: the drawer is out of flow, so reserving a column for
       it would leave a 280px hole down the left of every conversation. */
    grid-template-columns: none;
  }

  /* With the keyboard up the home indicator is behind the keyboard, not
     behind the composer, so the inset it reserves is a gap for nothing —
     and it is the gap that pushes the send button under the suggestion bar. */
  body.keyboard-up #composer { padding-bottom: 6px; }
  #main { height: 100%; }

  /* Positioned from `--sidebar-width` and `--rail-offset`, both of which
     describe columns that no longer exist here. */
  #reconnect-banner { left: 0; }

  /* ══ The swipe belongs to the app, never to the browser ══
     A horizontal overscroll is the back/forward navigation gesture. Going
     back lands on `/login`, which looks exactly like being signed out and is
     not — swiping the other way returns to the app with the session intact,
     which is worse than a clean failure because it teaches somebody the app
     logged them out at random.
     This is the declarative half and it is what Chrome and Android answer to;
     iOS Safari needs the gesture taken at `touchstart`, which `claimEdge()`
     in mobile.js does. Neither covers the other. */
  html, body { overscroll-behavior-x: none; }
  #app, #main, #pane-split, #pane-primary, #messages { overscroll-behavior-x: contain; }

  /* Declared in index.html and display:none everywhere else. */
  .mobile-only { display: flex; }

  /* ══ The bar ══
     Above the takeovers, and it stays through all of them: `#main.takeover`
     hides `#pane-header`, so the drawer button cannot live there. */

  #mobile-bar {
    display: flex;
    align-items: center;
    gap: 4px;
    flex: 0 0 auto;
    padding: 0 4px;
    padding-top: env(safe-area-inset-top);
    min-height: 52px;
    background: var(--bg-secondary);
    border-bottom: 1px solid var(--border);
    position: relative;
    z-index: 4;
  }

  .mobile-bar-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    /* 44px is the smallest target a thumb hits reliably. Everything tappable
       in this file is held to it. */
    width: 44px;
    height: 44px;
    flex: 0 0 44px;
    border-radius: 8px;
    color: var(--text-secondary);
  }
  .mobile-bar-btn:active { background: var(--bg-message); color: var(--text-primary); }

  .mobile-title {
    flex: 1;
    min-width: 0;
    font-weight: 700;
    font-size: 15.5px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* Under a takeover the screen carries its own heading and its own Done
     button. Two titles arguing is worse than one, so the bar keeps only the
     way out. */
  #main.takeover #mobile-title,
  #main.takeover #mobile-more-btn { visibility: hidden; }

  /* ══ Drawer ══ */

  /* Out of the flow entirely, sliding over the conversation. The rail is a
     separate element (a sibling of #sidebar, not a child), so it is fixed
     with the same transform and rides in alongside. */
  #sidebar,
  #app.spaces-mode #space-rail {
    position: fixed;
    left: 0;
    width: min(86vw, 320px);
    z-index: 45;
    transform: translateX(-100%);
    transition: transform 220ms cubic-bezier(.22, .61, .36, 1);
    will-change: transform;
  }
  body.drawer-open #sidebar,
  body.drawer-open #app.spaces-mode #space-rail { transform: none; }
  /* Mid-drag the finger sets the transform frame by frame; a transition on
     top of that is the layout arriving a fifth of a second after the thumb. */
  body.drawer-dragging #sidebar,
  body.drawer-dragging #app.spaces-mode #space-rail { transition: none; }

  #sidebar {
    top: 0;
    bottom: 0;
    border-right: 1px solid var(--border);
    box-shadow: 12px 0 32px rgba(0, 0, 0, 0.4);
    padding-top: env(safe-area-inset-top);
    padding-bottom: env(safe-area-inset-bottom);
  }

  /* Nothing to resize — the drawer's width is the screen's. */
  #sidebar-gutter { display: none; }

  /* The workspace switcher, and it is the same rail. A row rather than a
     column, pinned to the top of the drawer with the sidebar starting below
     it: two fixed elements moving as one, which is what lets the rail be a
     sibling of the sidebar in the markup and part of it on screen. */
  #app.spaces-mode #space-rail {
    top: 0;
    height: calc(78px + env(safe-area-inset-top));
    padding: 8px 6px 0;
    padding-top: calc(8px + env(safe-area-inset-top));
    flex-direction: row;
    gap: 6px;
    overflow-x: auto;
    overflow-y: hidden;
    border-right: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
    /* Six companies scroll rather than shrink: a monogram that has been
       squeezed to 20px is not a switcher, it is a colour. */
    scrollbar-width: none;
  }
  #app.spaces-mode #space-rail::-webkit-scrollbar { display: none; }
  #app.spaces-mode #space-rail .space-tab { flex: 0 0 auto; }

  /* Which space you are in. The desktop pill is a vertical bar on the tab's
     LEFT edge, which in a column reads as a marker and in a row reads as a
     divider between two tabs — it looked like a rule drawn before "You"
     rather than a mark on the tab beside it. Turned through ninety degrees
     with the rail: an underline along the bottom, which is what a row of
     tabs has always meant. */
  #app.spaces-mode #space-rail .space-tab.active::before {
    left: 8px;
    right: 8px;
    top: auto;
    bottom: 0;
    width: auto;
    height: 3px;
    border-radius: 3px 3px 0 0;
  }
  #app.spaces-mode #sidebar {
    top: calc(78px + env(safe-area-inset-top));
    padding-top: 0;
  }

  /* Tapping away is how a drawer closes on a phone. Present in the DOM only
     while it is open (mobile.js), so it can never swallow a tap on the
     conversation underneath. */
  #mobile-scrim {
    position: fixed;
    inset: 0;
    z-index: 44;
    background: rgba(0, 0, 0, 0.5);
    opacity: 0;
    transition: opacity 220ms ease;
  }
  body.drawer-open #mobile-scrim { opacity: 1; }
  body.drawer-dragging #mobile-scrim { transition: none; }

  /* The header shrinks: the account button is the only thing in it that does
     anything, and "OS/A" is on the sign-in page. */
  .ws-header { padding: 12px 16px 10px; }
  .side-search { padding: 0 12px 12px; }
  .side-search input { font-size: 16px; padding: 9px 10px; }

  /* Rows are the whole point of the drawer and they are what gets mis-tapped:
     a 26px row with a star and an unread badge on it has three targets inside
     a thumb's width. */
  .side-item { min-height: 44px; }
  .side-dashboard,
  .side-create-btn,
  .side-playbooks,
  .side-help { min-height: 44px; }
  /* Reordering is a drag on a desktop and a scroll here. The stars stay
     tappable; the drag handle has nothing to say on a touch screen. */
  .side-item .row-star { width: 32px; height: 32px; }

  /* ══ Conversation ══ */

  /* Almost all of it moves to the bar or the ⋯ menu. The member chips stay,
     because a channel member's DM is a real place and the old rule at this
     width deleted the only way in. */
  #pane-header {
    padding: 0;
    min-height: 0;
    border-bottom: none;
    gap: 0;
  }
  #pane-header > *:not(#pane-members) { display: none !important; }
  #pane-header:has(#pane-members:empty) { display: none; }

  .pane-members {
    flex-wrap: nowrap;
    overflow-x: auto;
    padding: 7px 12px;
    gap: 8px;
    border-bottom: 1px solid var(--border);
    scrollbar-width: none;
  }
  .pane-members::-webkit-scrollbar { display: none; }
  .pane-members > * { flex: 0 0 auto; }

  /* Behind the ⋯. Five labelled tabs do not fit 390px, and shrinking them
     until they do makes five things nobody can read. */
  #pane-tabs { display: none; }

  #messages { padding: 10px 0 4px; }
  #composer { padding: 4px 10px; padding-bottom: max(10px, env(safe-area-inset-bottom)); }

  /* Below 16px iOS Safari zooms the page on focus and does not zoom back:
     the composer fills the screen and the transcript is off the right edge
     until you pinch out by hand. Every text input here is 16px for the same
     reason. */
  #composer-input,
  input[type=text], input[type=search], input[type=email],
  input[type=password], input[type=number], textarea, select { font-size: 16px; }

  /* "Enter to send · Shift+Enter for a new line" — neither key is on this
     keyboard. A hint that describes a different machine is worse than none. */
  .composer-hint { display: none; }
  #send-btn, .fmt-btn { min-width: 40px; min-height: 40px; }

  /* Swipe transport. mobile.js sets the transform; the transition is only on
     for the commit and the spring back, never during the drag. */
  #pane-primary { min-width: 0; will-change: transform; }
  #pane-primary.swiping { transition: none; }
  #pane-primary.settling { transition: transform 180ms cubic-bezier(.22, .61, .36, 1); }

  /* Where the swipe is going, named before it gets there. Without it a swipe
     is a lottery, and nobody swipes twice on a lottery. */
  #swipe-hint {
    position: absolute;
    top: 8px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 6;
    max-width: 76vw;
    padding: 5px 13px;
    border-radius: 999px;
    background: var(--bg-message);
    border: 1px solid var(--border);
    color: var(--text-secondary);
    font-size: 12.5px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    pointer-events: none;
    opacity: 0;
    transition: opacity 120ms ease;
  }
  #swipe-hint.on { opacity: 1; }
  #swipe-hint strong { color: var(--text-primary); font-weight: 600; }

  /* ══ Off here ══
     Not shrunk. A lane on a 390px screen is not a smaller split view, it is
     an unreadable one; and the assisted-editing panel is a second column
     beside a document that is already the whole screen. mobile.js also stops
     `Relay.lanes.open` — a lane hidden by CSS would still hold a `view`
     subscription for a pane nobody can see. */
  #side-lanes,
  #btn-split,
  #assist-toggle,
  .assist-panel,
  .assist-gutter { display: none !important; }

  /* ══ Takeovers ══
     Dashboard, Account, Agents, All files, Help. Made usable, not redesigned:
     one column, nothing wider than the screen, tab strips that scroll. */

  .agents-head {
    flex-wrap: wrap;
    gap: 10px;
    padding: 12px 14px;
  }
  .agents-head h2 { font-size: 16px; }
  /* Two lines of explanation above a screen you have already chosen to open,
     on a screen this size, is the top third of it. */
  .agents-sub { display: none; }
  .agents-head-tools { margin-left: 0; width: 100%; flex-wrap: wrap; }
  .agents-head-tools input[type=text] { width: 100%; }
  .agents-body { padding: 12px 14px; }

  /* The Files and Topics toolbars are a filter box and three or four buttons
     on one row. At 360px the last button is off the right-hand edge, and
     `body` is `overflow: hidden`, so it is not merely awkward to reach — it
     is gone. They wrap, and the filter takes the first line to itself. */
  .files-toolbar, .topics-toolbar, .snippets-toolbar { flex-wrap: wrap; gap: 8px; }
  .files-toolbar input, .topics-toolbar input, .snippets-toolbar input { flex: 1 0 100%; }
  .files-toolbar .btn, .topics-toolbar .btn, .topics-toolbar select, .snippets-toolbar .btn { min-height: 40px; }
  /* A snippet row's actions cannot be hovered into view on a phone. */
  .snippet-acts { opacity: 1; flex-wrap: wrap; justify-content: flex-end; max-width: 40%; }

  /* Ten account tabs, and every other row of tabs in the takeovers. */
  .acc-tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .acc-tabs::-webkit-scrollbar { display: none; }
  .acc-tab { flex: 0 0 auto; min-height: 44px; }

  /* Multi-column blocks that already collapse at wider breakpoints in
     relay.css, brought down to this one. `.account-facts` is deliberately NOT
     here: it is a label/value pair with a 120px label, which still leaves the
     value 240px, and stacking a label above its value makes a fact list twice
     as long to read rather than easier. (`.usage-facts` is the same shape and
     relay.css already collapses it at 620px — a component deciding its own
     narrow behaviour is exactly what should stay there.) */
  .field-row,
  .dbc-split,
  .llm-detail-charts { grid-template-columns: 1fr; }
  .usage-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* A table cannot be reflowed honestly — the columns mean something — so it
     keeps its shape and scrolls inside its own box. `body` is already
     `overflow: hidden` in relay.css, which means a wide table without this is
     not scrolled sideways but CLIPPED, and the last two columns simply are
     not there. */
  table { display: block; overflow-x: auto; }
  pre { overflow-x: auto; }

  /* ── Account → LLM Config ──
     The card grid is `minmax(340px, 1fr)`, wider than a 360px phone once the
     panel's padding is taken off, so the whole tab slid sideways. One column,
     and rows sized by their own card rather than by the tallest. */
  #acc-llm-list { grid-template-columns: minmax(0, 1fr); grid-auto-rows: auto; }
  .llm-card { min-width: 0; }
  .llm-card-spend { flex-wrap: wrap; }
  .llm-card-spend select { max-width: 100%; }
  .llm-card-actions { flex-wrap: wrap; }
  /* The intro sat in a column beside four buttons that refused to shrink,
     so it was one word wide. Buttons go under it and wrap. */
  .llm-toolbar { flex-direction: column; gap: 10px; }
  .llm-toolbar-actions { flex-wrap: wrap; flex-shrink: 1; }
  .llm-detail-head { flex-wrap: wrap; }
  /* Opened under its card (account.js moves it there on a phone), so it
     spans the column it is placed in. */
  #acc-llm-list > #acc-llm-detail { margin-top: 0; padding: 14px; }
  .llm-bar-row { grid-template-columns: 72px minmax(0, 1fr) 64px; gap: 8px; }
  .llm-detail-table { display: block; overflow-x: auto; }

  /* ── Account → Memory ──
     A row was one flex line: the labels never shrink, so the subject — the
     part being read — was left one character wide. The subject takes the
     first line beside its tick; the labels wrap under it. */
  .mem-head { flex-wrap: wrap; row-gap: 4px; }
  .mem-subject { flex: 1 1 calc(100% - 30px); min-width: 0; }
  .mem-toolbar { flex-wrap: wrap; }
  .mem-toolbar input:not([type=checkbox]) { flex: 1 1 calc(100% - 40px); }

  /* ══ Modals and menus ══ */

  /* A dialog centred in a phone screen with a keyboard up is a dialog behind
     the keyboard. A sheet grows from the edge the thumb is already at.

     And it has to be sized against the VISUAL viewport, not `dvh`. The
     modals sit outside `#app`, so the `--vvh` sizing above does not reach
     them, and `position: fixed` measures against the layout viewport, which
     on iOS does not shrink for a keyboard at all. So a sheet at `bottom: 0`
     with `max-height: 85dvh` was half behind the keyboard and taller than the
     part of the screen still visible: its body never overflowed, so it never
     scrolled, and the only way to reach the rest was Safari panning the whole
     page — which `fitToKeyboard()` undoes on every visualViewport scroll.
     That is the bounce: every drag on the create channel form moved the page
     and was put straight back. `--kb` (mobile.js) is the keyboard's height,
     0 when it is down; the sheet stands on it and fits inside what is left,
     so its body overflows and scrolls like anything else. */
  .modal {
    top: auto;
    bottom: var(--kb, 0px);
    left: 0;
    transform: none;
    width: 100%;
    max-width: none;
    max-height: calc(var(--vvh, 100dvh) * 0.85);
    border-radius: 12px 12px 0 0;
    border-bottom: none;
    padding-bottom: env(safe-area-inset-bottom);
  }
  .modal-actions { flex-wrap: wrap; }
  .modal-actions .btn { min-height: 44px; }

  /* Opened by a long press (mobile.js), positioned at the touch point by
     `build()` in contextmenu.js, which already clamps it to the viewport. It
     only needs to stop being desktop-sized. */
  .ctx-menu { max-width: 88vw; }
  .ctx-item { min-height: 44px; display: flex; align-items: center; }

  /* A long press that opens a menu must not also select the word underneath
     it, or the menu arrives on top of a blue highlight and a set of drag
     handles. Inputs are exempt — selecting text is what they are for. */
  #sidebar, #messages { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
  #sidebar input, #messages input, #messages textarea { -webkit-user-select: text; user-select: text; }

  .btn { min-height: 40px; }

  /* Uniform card height is a desktop grid property. In one column it means
     every card is as tall as the tallest, which on a phone is most of the
     screen given to whitespace. */
  #acc-llm-list { grid-auto-rows: auto; }
}

/* Declared outside the query on purpose: these elements exist in index.html
   at every width, and a phone-only control on a desktop is a control nobody
   asked for. The rule above turns them on. */
.mobile-only { display: none; }
