/*
 * Virtyal AI — mobile layer.
 *
 * The pages are built from inline styles, so every rule here needs !important to win.
 * Loaded by every page; mobile.js supplies the burger and the chat drawer button.
 */

/* ---------- chat: keep the composer pinned to the bottom ----------
   The chat scrolls its whole column — messages AND composer — so once the thread grows
   the input slides out of view and leaves dead space behind it. Move the scrolling to
   the message list only. Applies at every width: it is a bug on desktop too. */
[data-screen-label="Chat"] div[style*="overflow:auto"],
[data-screen-label="Chat"] div[style*="overflow: auto"] {
  overflow: hidden !important;
}
[data-screen-label="Chat"] div[style*="padding:24px 26px"],
[data-screen-label="Chat"] div[style*="padding: 24px 26px"] {
  overflow-y: auto !important;
  min-height: 0 !important;
}

/* The phone lays the thread out at full height and scrolls the page itself —
   fighting that with fixed heights kept losing: the composer sailed off with
   the page, five fixes in a row. Align with the browser instead: let the page
   grow and pin the glass with sticky. min-height keeps a short thread's glass
   at the bottom edge rather than mid-air; dvh follows the browser's own bars. */
@media (max-width: 880px) {
  /* Gated like the overflow pair below, and for the same incident: framed,
     height:auto re-grows the column to the thread's full length — flex:1
     with min-height:0 constrains nothing inside an auto-height parent — so
     the frame's document towered over the frame again and Safari's focus
     scroll had its plaything back. The first fix missed this because the
     harness thread was short and min-height dominated; the owner's long
     thread found it within the hour. Framed, the root keeps its inline
     height:100vh, which inside an iframe IS the frame's own height. */
  html:not(.vm-embedded) [data-screen-label="Chat"][style*="height:100vh"],
  html:not(.vm-embedded) [data-screen-label="Chat"][style*="height: 100vh"] {
    height: auto !important;
    min-height: 100dvh !important;
  }
  /* The pane over the glass was overflow:hidden — a scroll container, which
     is what position:sticky quietly attaches to. Grown to the thread's full
     height it took the glass along. Visible overflow hands sticky back to
     the screen's own viewport.

     Gated to the STANDALONE page on purpose. Inside the project's iframe the
     same override made the frame's internal document the scroller — a long
     thread laid out at full height inside a fixed box — and iOS Safari's
     scroll-into-view on focus is known to shove such a scroller past its
     end and leave it there: the composer surfaced near the top of the frame
     with the document's empty tail as a black void below it, which is
     exactly what the owner photographed. Framed, the messages block keeps
     its own inline overflow-y:auto, the document is never taller than the
     frame, and the focus scroll has nothing to wreck. */
  html:not(.vm-embedded) [data-vm-pane] { overflow: visible !important; }
  html:not(.vm-embedded) [data-msgs] { overflow-y: visible !important; }

  /* --- the bar steps aside for the keyboard --- */
  /* With the keys up a phone keeps ~400px of glass, and the bar was
     spending a whole row of it on furniture nobody taps mid-sentence:
     burger, logo, the lifted Чати/Розмови, the standalone Чати button.
     mobile.js sets vm-kb while the keyboard stands; fitConv re-measures on
     the same tick, so the freed row goes straight to the thread. The
     keyboard closes, the class drops, everything returns. */
  html.vm-kb nav { display: none !important; }
  html.vm-kb [data-vm-tabs].vm-tabs-collapsed { display: none !important; }
  html.vm-kb .vm-chat-open { display: none !important; }

  /* The drawer+pane row must never wrap. The generic phone rule below wraps
     every inline-styled flex row — and in a WRAPPING row, stretch sizes each
     line to its content, so the pane grew to the thread's full 6300px inside
     a 771px frame and carried the composer past the fold. Nowrap hands the
     height back to stretch: pane = row = frame, and the messages block is
     the one that scrolls. */
  [data-screen-label="Chat"] div:has(> [data-vm-pane]) { flex-wrap: nowrap !important; }
}

/* The belt to the height fixes' braces: whatever a phone browser makes of the
   column heights — some lay the whole thread out at full length and scroll
   the page itself — a sticky glass cannot leave the screen. When the heights
   are right this is a no-op; when they are not, the composer, the pickers and
   the gear still ride the bottom edge, the way the owner asked: "неважливо,
   чи я пішов до верха — вони в час мають бути в екрані". */
@media (max-width: 880px) {
  /* The composer floats OVER the thread now instead of standing on a shelf
     of its own — the owner, with Perplexity's on screen: «блок весь снеси,
     знизу лиши відступ, з боку лишай відступ… щоб текст зникав, коли
     заходить під панельку».
     Absolute over the pane, not sticky beside the feed: sticky still costs
     the feed its own height, which was the 169px slab of dead black under
     the line. The glass keeps no fill and no border of its own — only the
     composer box inside it stays drawn — and its gradient is what the text
     dissolves into on the way under. backdrop-filter blurs whatever is
     still showing through the transparent top of that gradient. */
  [data-vm-pane] { position: relative !important; }
  [data-vm-glass] {
    position: absolute !important;
    left: 0 !important;
    right: 0 !important;
    bottom: 0 !important;
    z-index: 40 !important;
    border-top: none !important;
    /* 30px of fade above the field read as another empty band — «та верхня
       хуйня дуже довго». 9px is enough to soften the edge the text passes
       under and no more; below it the composer box's own fill takes over,
       so the thread slides straight into «Напишіть команду агенту». */
    padding: 9px 13px 12px !important;
    background: linear-gradient(to bottom, transparent, var(--bg) 92%) !important;
    -webkit-backdrop-filter: blur(9px);
    backdrop-filter: blur(9px);
  }
  /* The feed ends above the floating glass rather than behind it, so the
     last line can always be scrolled clear. The height is measured by
     mobile.js — the composer grows as it is typed into. */
  /* Two attributes deep on purpose: the generic phone rule that re-pads
     every inline-styled 24px/26px box carries !important too and sits later
     in this file, so an equal-specificity selector here loses the tie and
     the feed keeps a 16px bottom — measured, the last line stayed under the
     glass. */
  [data-vm-pane] [data-msgs] { padding-bottom: var(--vai-glass-h, 130px) !important; }
}


/* ---------- burger button, hidden on desktop ---------- */
.vm-burger {
  display: none;
  width: 36px;
  height: 34px;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line2);
  border-radius: 10px;
  background: var(--panel);
  color: var(--t1);
  cursor: pointer;
  padding: 0;
}
.vm-burger svg { display: block; }

/* ---------- drawer scrim ---------- */
.vm-scrim {
  position: fixed;
  inset: 0;
  z-index: 88;
  background: rgba(3, 6, 12, .55);
  backdrop-filter: blur(2px);
  opacity: 0;
  pointer-events: none;
  transition: opacity .2s ease;
}
.vm-scrim.is-open { opacity: 1; pointer-events: auto; }

/* ---------- chat drawer handle ---------- */
.vm-chat-open {
  display: none;
  align-items: center;
  gap: 8px;
  padding: 8px 13px;
  margin: 10px 0 0 12px;
  align-self: flex-start;
  border: 1px solid var(--line2);
  border-radius: 10px;
  background: var(--panel);
  color: var(--t1);
  font-family: 'Manrope', sans-serif;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
}

/* The shelf exists in the DOM at every width — mobile.js appends it without
   asking — but it is a PHONE menu's bottom shelf and nothing else. Without
   this base rule it surfaced on the desktop as two bare text rows tacked
   onto the inline nav, «Змінити тему» and «Вхід» beside the links, while
   the bar's own sun and Вхід stood right there. The owner's instruction was
   explicit: the desktop was not to change at all. Above the media block on
   purpose — the ≤880px rules that turn the shelf back on inside the
   dropdown must come later in the file to win the tie. */
.vm-nav-extra { display: none; }

@media (max-width: 880px) {
  html, body { overflow-x: hidden !important; }

  /* ---------- the tap that used to walk the page off the screen ----------
     iOS Safari zooms the whole page IN when a focused field's font is under
     16px. It is a built-in accessibility courtesy and there is no opting out
     of it — except by not being under 16px.
     That zoom, not a broken layout, is what the owner kept reporting: tap the
     field, the page scales past the edge and the text is cut off on the right;
     the composer slides out of view; dismiss the keyboard and the zoom STAYS,
     so everything is left sitting somewhere else. Measured 2026-08-13 —
     fifteen kinds of field across nineteen screens, every one of them under
     16px, so every field on the site did this: the chat, the login, the
     registration, every search box.
     Four rounds of height math were spent fighting the symptom. The keyboard
     was never the problem; the zoom was.
     !important because the sizes are written inline on each field. The types
     that raise no keyboard are left alone — a 16px range slider or checkbox
     would only distort a row for nothing. */
  input:not([type='checkbox']):not([type='radio']):not([type='range']):not([type='color']):not([type='file']),
  textarea,
  select,
  [contenteditable=''],
  [contenteditable='true'] {
    font-size: 16px !important;
  }

  /* ---------- and the field it typed into gets a row of its own ----------
     Four controls abreast — attach, field, mic, send — left the field 193px
     on a 393px screen. Measured, not guessed: type a normal sentence into it
     and scrollWidth reads 213 against a 193px box, so the words you just
     wrote slide out of sight to the left as you write them. Raising the font
     to 16px above made that worse, not better: same box, bigger letters.
     The React panel's composer has wrapped like this since Stage 2 — the
     drawn one never learned. The field takes the top row whole; attach, mic
     and send share the one under it. */
  /* The composer is ONE row on a phone, GPT's compact shape: + on the left,
     the field between, mic and the send arrow together on the right. This
     used to be unreadable — a plain input scrolled its text sideways out of
     a 193px box — and the field was given a row of its own to survive. The
     field is a growing textarea now: text wraps and the box rises, so the
     narrow seat costs nothing and the whole glass stays two rows tall.
     No rule needed for the shape itself — the row lays its children out in
     DOM order, which already reads + field mic send. */

  /* The mic-picker ▾ steps off the phone: a phone has the one microphone it
     was built with, so a chooser is furniture — and its 14px were the
     difference between the placeholder fitting whole and wrapping onto a
     clipped second line. The desktop keeps it; headsets are a desktop thing.
     The gap tightens for the same seat: four controls at 10px spend 30px of
     a 393px screen on air. */
  [data-vm-row] button[aria-label="Вибрати мікрофон"] { display: none !important; }
  [data-vm-row]:has(> [data-draft-input]) { gap: 7px !important; }

  /* The glass, condensed: the phone has no room for a bar of furniture
     between the thread and the keys. The meter's whole row goes — its
     numbers stay one tap away inside the gear. The desktop keeps the row:
     the owner asked for it to stay there. The gear needs no margin rule any
     more — it sits in the composer's controls row with its own inline
     margin, claiming the slack before the mic. */
  [data-vm-meter-row] { display: none !important; }

  /* --- nav: links collapse into a dropdown --- */
  /* Phone header order: burger hard left, logo centred, actions right.
     mobile.js inserts the burger right after the logo, so `order` does the rearranging
     without touching each page's markup. */
  nav {
    position: sticky !important;
    padding: 11px 12px !important;
    gap: 8px !important;
  }
  .vm-burger { display: flex; order: -1; }
  nav > a:first-of-type {
    order: 0;
    flex: 1 1 auto;
    justify-content: center;
  }
  nav > div:last-child { order: 1; }

  [data-vm-nav] {
    display: none !important;
    position: absolute !important;
    top: 100% !important;
    left: 0 !important;
    right: 0 !important;
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 2px !important;
    padding: 10px !important;
    /* solid, not the nav's translucent fill — the hero globe showed through and
       made the menu hard to read */
    background: var(--panel) !important;
    border-bottom: 1px solid var(--line) !important;
    box-shadow: 0 22px 44px rgba(0, 0, 0, .4);
  }
  [data-vm-nav].is-open { display: flex !important; }
  [data-vm-nav] a {
    padding: 12px 13px !important;
    border-radius: 10px !important;
    font-size: 15px !important;
  }

  /* --- the burger menu's bottom shelf: theme and account --- */
  /* The bar's own copies hide below; these rows (built by wireNavExtra)
     forward the tap to them, so the logic stays one implementation. The
     :not guard matters — on a screen without the actions div, a bare
     div:last-child would be the dropdown itself, and the menu would
     swallow its own links. */
  nav > div:last-child:not([data-vm-nav]) { display: none !important; }
  .vm-nav-extra {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid var(--line, #2a2f3a);
  }
  .vm-nav-extra-row {
    display: block;
    width: 100%;
    padding: 12px 13px;
    border: none;
    border-radius: 10px;
    background: none;
    color: var(--t2, #aeb6c4);
    font-family: 'Manrope', sans-serif;
    font-size: 15px;
    font-weight: 600;
    text-align: left;
    cursor: pointer;
  }
  .vm-nav-extra-row:hover { background: var(--panel2, #1a1f29); color: var(--t1, #e8ecf3); }

  /* --- the chat's header row moves up into the freed bar --- */
  /* With the avatar and theme in the menu, the bar's right side stands
     empty — exactly the room «Чати» and «Розмови ▾» were spending a row
     on. Fixed, not reparented: the strip is React's node, and the page
     under it has nothing to scroll on the chat tab (fitConv sizes it to
     the screen), so fixed sits still. Gated on [data-vm-conv]: any other
     tab keeps its strip where it always was. And gated OFF the design
     address, where the mockup badge occupies the top edge and top:8px
     would tuck the strip underneath it — the shop window keeps its row. */
  body:not(:has([data-mockup-badge])):has([data-vm-conv]) [data-vm-tabs].vm-tabs-collapsed {
    position: fixed !important;
    top: 8px !important;
    right: 10px !important;
    left: auto !important;
    /* Above the bar's own 50 — under it the strip rendered fine and showed
       nothing, hidden behind the bar's opaque fill. Below the drawer's 90. */
    z-index: 60;
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    background: none !important;
    overflow: visible !important;
  }
  /* While chat controls ride the bar, the logo hugs the burger on the left
     instead of centring — centred, the wordmark ran under «Чати». */
  body:not(:has([data-mockup-badge])):has([data-vm-conv]) nav > a:first-of-type,
  body:not(:has([data-mockup-badge])):has(nav):has([data-vm-glass]) nav > a:first-of-type {
    flex: 0 0 auto !important;
    justify-content: flex-start !important;
    /* The bar is justify-content:space-between: shrunk to its content, the
       logo's next stop is the RIGHT edge — straight under the lifted strip.
       The auto margin claims the slack on its right and keeps it by the
       burger. */
    margin-right: auto !important;
  }

  /* The standalone chat has no tab strip — its «Чати» button rides up
     alone. Only where the bar exists: inside the project's frame the bar
     is hidden, and the button keeps its seat by the thread. */
  body:has(nav):not(:has([data-mockup-badge])) .vm-chat-open {
    position: fixed;
    top: 10px;
    right: 10px;
    z-index: 60;
    margin: 0;
  }

  /* --- chat: the sidebar becomes a drawer --- */
  [data-vm-drawer] {
    position: fixed !important;
    top: 0 !important;
    bottom: 0 !important;
    left: 0 !important;
    z-index: 90 !important;
    width: min(300px, 86vw) !important;
    transform: translateX(-102%);
    transition: transform .22s ease;
    box-shadow: 0 0 60px rgba(0, 0, 0, .5);
  }
  [data-vm-drawer].is-open { transform: translateX(0); }
  .vm-chat-open { display: inline-flex; }
  .vm-drawer-close { display: grid !important; }
}

.vm-drawer-close {
  display: none;
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 2;
  width: 32px;
  height: 32px;
  place-items: center;
  border: 1px solid #39414f;
  border-radius: 9px;
  background: #1b2129;
  color: #c7ceda;
  cursor: pointer;
  padding: 0;
}

@media (max-width: 880px) {
  [data-vm-drawer] { position: fixed !important; }

  /* --- two-column card grids fall back to one --- */
  [data-vm-cards] { grid-template-columns: 1fr !important; }
}

/* ---------- rows that would otherwise run off the edge ----------
   NOTE: DCLogic rewrites inline styles through the DOM, so the style attribute ends up
   normalised with spaces ("display: flex"). Every attribute selector below therefore has
   to match both spellings — matching only the source spelling silently does nothing. */
@media (max-width: 880px) {
  [data-screen-label] div[style*="display:flex"]:not([style*="flex-direction:column"]):not([data-vm-nav]):not([data-vm-tabs]),
  [data-screen-label] div[style*="display: flex"]:not([style*="flex-direction: column"]):not([data-vm-nav]):not([data-vm-tabs]) {
    flex-wrap: wrap;
  }

  /* An input row is always "[button] [field] [button]" — wrapping it puts each control
     on its own line and turns the composer into a three-storey block. */
  [data-vm-row], [data-screen-label] div:has(> input) {
    flex-wrap: nowrap !important;
  }

  /* Tab strips swipe sideways instead of stacking into two rows — a second row of tabs
     eats vertical space and reads as clutter on a phone. */
  [data-vm-tabs] {
    flex-wrap: nowrap !important;
    overflow-x: auto;
    scrollbar-width: none;
    -ms-overflow-style: none;
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity;
  }
  [data-vm-tabs]::-webkit-scrollbar { display: none; }
  [data-vm-tabs] > * {
    flex: 0 0 auto !important;
    scroll-snap-align: start;
  }
  /* long unbroken strings (paths, hashes, ids) must not push the layout open */
  [data-screen-label] span, [data-screen-label] div, [data-screen-label] a {
    min-width: 0;
    overflow-wrap: anywhere;
  }

  /* decorative background glows are sized in fixed px and stick out */
  [style*="width:640px"], [style*="width: 640px"] { max-width: 100vw !important; }
}

/* ---------- collapsed tab strips (phones) ----------
   mobile.js folds a strip that genuinely overflows into one button naming
   the active tab; the full list opens as a dropdown underneath. On the
   project page the «Чати» button joins the same row (wireConvChats), so the
   chat below starts clean instead of stacking a third row of furniture. */
.vm-tabs-pick, .vm-tabs-menu, .vm-tabs-chats { display: none; }

@media (max-width: 880px) {
  [data-vm-tabs].vm-tabs-collapsed {
    overflow: visible !important;
    position: relative;
  }
  [data-vm-tabs].vm-tabs-collapsed > button:not(.vm-tabs-pick):not(.vm-tabs-chats) {
    display: none !important;
  }
  /* «Чати» stands left — its drawer slides in from the left edge, and a
     button on the right that opens the left side reads backwards. The tab
     picker takes the right, and its list drops under the right edge. */
  .vm-tabs-pick {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    margin-left: auto;
    padding: 8px 14px;
    border: none;
    border-radius: 9px;
    background: var(--panel2);
    color: var(--t1);
    font-family: 'Manrope', sans-serif;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
  }
  .vm-tabs-menu {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    min-width: 190px;
    z-index: 70;
    flex-direction: column;
    gap: 2px;
    padding: 6px;
    border: 1px solid var(--line2);
    border-radius: 12px;
    background: var(--panel);
    box-shadow: 0 18px 40px rgba(0, 0, 0, .45);
  }
  .vm-tabs-menu.is-open { display: flex; }
  .vm-tabs-menu button {
    text-align: left;
    padding: 10px 12px;
    border: none;
    border-radius: 8px;
    background: none;
    color: var(--t2);
    font-family: 'Manrope', sans-serif;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
  }
  .vm-tabs-menu button.is-active { background: var(--panel2); color: var(--t1); }
  .vm-tabs-chats {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    order: -1;
    padding: 7px 12px;
    border: 1px solid var(--line2);
    border-radius: 9px;
    background: var(--panel);
    color: var(--t2);
    font-family: 'Manrope', sans-serif;
    font-size: 12.5px;
    font-weight: 700;
    cursor: pointer;
  }
  /* Inside a project the tab row owns the button; the frame's own copy
     would be a second burger a centimetre below the first. */
  :root.vm-embedded .vm-chat-open { display: none !important; }
}

/* Phones proper */
@media (max-width: 620px) {
  /* ---------- full bleed ----------
     On a 390px screen a 26px gutter plus a 1px frame costs ~14% of the width for
     decoration. Everything that looks like a window on desktop goes edge to edge. */
  [data-screen-label="Mockup"] {
    padding: 0 !important;
  }
  [data-screen-label="Mockup"] > div {
    border: none !important;
    border-radius: 0 !important;
    max-width: none !important;
    max-height: none !important;
    box-shadow: none !important;
  }
  iframe {
    border: none !important;
    border-radius: 0 !important;
  }
  /* An embedded chat pinned to 640px either scrolls internally or leaves dead space
     under the composer. Let it take the screen instead, so the input sits at the bottom. */
  [style*="height:640px"], [style*="height: 640px"] {
    height: calc(100vh - 190px) !important;
    min-height: 420px !important;
  }
  /* content gutters shrink but do not vanish — text on the very edge is unpleasant */
  [style*="padding:24px 26px"], [style*="padding: 24px 26px"],
  [style*="padding:20px 22px"], [style*="padding: 20px 22px"] {
    padding: 16px 13px !important;
  }

  /* The project's Розмови tab: the embedded chat brings its own gutter, so
     the tab escapes the page's — otherwise the two stack and the text sits a
     doubled inset from the edge, framed twice. */
  [data-vm-conv] {
    margin-left: -13px !important;
    margin-right: -13px !important;
    margin-bottom: -16px !important;
  }

  /* On a phone the Розмови tab is the whole screen: the header rows hide
     and the tab takes the room under the nav and the tab strip, so the page
     itself has nothing to scroll and the composer stays on the glass. */
  [data-screen-label="Mockup"]:has([data-vm-conv]) [data-vm-proj-head] {
    display: none !important;
  }
  [data-vm-conv] {
    height: calc(100dvh - 126px) !important;
    min-height: 0 !important;
    /* Negative, to eat the tab wrapper's own 16px top padding: measured 24px
       of black between the bar's line and the first message, and the owner
       counted it as dead — «оте чорне під рисочкою можна забирати». The
       thread starts at the line now; fitConv reads the new top and hands the
       reclaimed rows to the feed. */
    margin-top: -16px !important;
    margin-bottom: -16px !important;
  }
  [data-vm-tabs][style*="padding:12px 22px"],
  [data-vm-tabs][style*="padding: 12px 22px"] {
    padding: 10px 13px !important;
  }

  /* Popovers anchored to a control in the middle of the bar run off the right edge on a
     phone. Pin them across the viewport instead. */
  [data-vm-popover] {
    position: fixed !important;
    left: 8px !important;
    right: 8px !important;
    bottom: 84px !important;
    top: 70px !important;
    width: auto !important;
    max-width: none !important;
    max-height: none !important;
  }

  h1 { font-size: clamp(30px, 9vw, 44px) !important; }

  /* 11px monospace is unreadable on a phone; lift the floor.
     10.5px is the chat's — the ОБРАНІ / ЗАКРІПЛЕНІ / МОДЕЛЬ / ДУМАННЯ captions
     and the context sizes beside each model, seventeen of them on that screen
     alone.
     Each selector twice: DCLogic rewrites inline styles through the DOM, so the
     attribute reads "font-size: 11px" with a space in the browser however it is
     written in the file, and a single spelling silently matches nothing. */
  [style*="font-size:10.5px"], [style*="font-size: 10.5px"],
  [style*="font-size:11px"],   [style*="font-size: 11px"],
  [style*="font-size:11.5px"], [style*="font-size: 11.5px"] { font-size: 12.5px !important; }

  /* the terminal prompt alone eats the width — shrink the whole console a notch */
  [data-screen-label="Terminal"] [style*="JetBrains Mono"] { font-size: 12px !important; }
  [data-screen-label="Terminal"] input { min-width: 90px !important; }

  /* A card row built as "name — detail — badge" cannot fit across a phone; squeezing it
     makes the labels collide, so stack it. The `flex:1` child is what identifies such a
     row — without that test this also caught short meta lines like "● Claude · 14:32"
     and split them over three lines. */
  [data-screen-label] div[style*="align-items:center"]:has(> [style*="flex:1"]):has(> :nth-child(3)):not([style*="flex-direction"]):not([data-vm-row]),
  [data-screen-label] div[style*="align-items: center"]:has(> [style*="flex: 1"]):has(> :nth-child(3)):not([style*="flex-direction"]):not([data-vm-row]) {
    flex-direction: column !important;
    align-items: flex-start !important;
    gap: 6px !important;
  }

  /* Code lines keep their natural width so the viewer's own overflow:auto can scroll
     them. Without this the generic min-width:0 above squeezes each line and the tail
     of long ones is simply lost. */
  [data-screen-label="Files"] div[style*="white-space:pre"],
  [data-screen-label="Files"] div[style*="white-space: pre"] {
    min-width: max-content !important;
  }
}
