/* --- Input Area --- */
/* A composer row belongs to a session's chat. On the dashboard it is a row of
   dead buttons under a list of cards, and in Files a status line about a
   conversation that is not on screen — which is what `.in-chat` (inSession &&
   view === "chat", set in syncMobileBar) already says.
   This lived inside the max-width:768px block, so it only ever ran on a phone
   and the desktop dashboard kept a disabled composer and a status bar describing
   whichever session you had open last. The rule was never about width; it was
   about WHERE you are, and that is the same on every screen. */
/* Every row of it, not just the box and the status line. Measured in Files on a
   1280px screen: the composer and the status bar had gone, and 212px of pins,
   notes, wake-ups, services, jobs and prompt buttons had not — six rows that
   describe a conversation you are not looking at, stacked under a file tree.
   They are hidden on the dashboard by being empty there; this says it as a rule
   instead of relying on that. */
body:not(.in-chat) #status-bar,
body:not(.in-chat) #input-area,
body:not(.in-chat) #owed-bar,
body:not(.in-chat) #pins-bar,
body:not(.in-chat) #wakeups-bar,
body:not(.in-chat) #services-bar,
body:not(.in-chat) #jobs-bar,
body:not(.in-chat) #inbox-bar,
body:not(.in-chat) #prompt-buttons-bar,
body:not(.in-chat) #prompt-row {
  display: none !important;
}

#input-area {
  display: flex;
  align-items: flex-end;
  /* Wrapping is for the reply chip and nothing else: it takes a whole line
     (`flex-basis: 100%` below) and so pushes the box and its buttons onto the
     next one. Everything else on this row is a fixed-size button beside a box
     that shrinks, so there is nothing here that wraps of its own accord — the
     phone's layout already relies on that, with the same declaration in the
     media query below. */
  flex-wrap: wrap;
  gap: 8px;
  padding: 8px 16px;
  background: var(--bg-surface);
  border-top: 1px solid var(--border);
  flex-shrink: 0;
}

/* WHAT THIS MESSAGE ANSWERS, while it is being written: one line at the top of
   the composer's frame, the full width of it, above the box.
   The quote button has no strip to share (it appends into the box itself), so
   this is a chip of its own — and the frame it lives in is the composer's rather
   than a bar of its own above it, because it is part of the message being
   written and goes away with it. */
#reply-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 100%;
  min-width: 0;
  color: var(--text-muted);
  font-size: 12px;
  line-height: 1.5;
}
#reply-chip.hidden { display: none; }
/* The preview is the part that gives: the mark and the ✕ keep their size and the
   text ellipsizes into whatever is left. */
#reply-chip-text {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#reply-chip .icon { flex: none; }
#reply-chip-clear {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 2px;
  background: none;
  border: none;
  border-radius: 4px;
  color: var(--text-muted);
  cursor: pointer;
}
@media (hover: hover) {
  #reply-chip-clear:hover { color: var(--text); }
}
/* A thumb's target on a phone, the way every other small control here gets one.
   It does not grow the line: the chip's own height is the text's, and the button
   is centred on it. */
@media (pointer: coarse) {
  #reply-chip-clear { min-width: 44px; min-height: 32px; }
}

/* The draft buttons are boxed in the markup so a phone can give them a line as a
   group (see #composer-draft in the media query below). Here there is nothing to
   wrap and every rule around them is written against the buttons themselves, so
   the box gets out of the way entirely. */
#composer-draft { display: contents; }
/* Clear is a phone's button (see the media query below). Here the row is inline
   with the box, and an ✕ beside a text box reads as "close", not "empty it".
   Scoped by its group because the dressing it shares with the other draft
   buttons (12-dialogs.css) loads later and says `inline-flex`. */
#composer-draft #clear-btn { display: none; }

#message-input {
  flex: 1;
  resize: none;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 12px;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.4;
  max-height: 120px;
  outline: none;
  transition: border-color 0.15s;
}
#message-input:focus { border-color: var(--accent); }
#message-input:disabled { opacity: 0.5; }

/* On mobile the side buttons squeeze the textarea down to ~10ch — wrap
   them under the input instead. The textarea spans the full first row,
   the surrounding buttons sit on a second row with send pushed right. */
@media (max-width: 768px) {
  /* THREE LINES, always, in DOM order — write it, say where it goes, go and look
     at something:
       [box ...................................]
       [✕ .......................... ⏰ ✉ 📌 ➤]
       [sections ..............]
     The box and the draft buttons each take a whole line (flex-basis 100%), so
     the sections can never wrap up beside either of them.
     They used to share line two whenever they fitted — five marks at 390 did,
     eight did not, and at 320 nothing did — which meant the row changed shape
     as counts came and went, and a list mark could stand a few pixels from ➤.
     The owner asked for them apart. That arrangement needed the row REVERSED (so
     that a shared line put ➤ at the right-hand end and wrapping dropped the
     sections rather than the buttons) and `order: -1` to keep the box on top of
     a reversed row; with nothing ever sharing a line there is no case left for
     either to answer, so the row reads left to right like everything else.
     The box and the buttons were briefly on ONE line together, which is what
     squeezed it to 132px at 320 and put ➤ where a thumb does not expect it.
     They are two acts, not one. */
  #input-area { flex-wrap: wrap; gap: 6px; padding: 6px 10px; }
  #message-input { flex-basis: 100%; max-width: 100%; }
  /* A real box on a phone, a whole line wide. What to do with the draft sits at
     the right, under the thumb that sends; the one way to throw it away sits
     alone at the far left, as far from ➤ as the line allows — the opposite act
     at the opposite end. #clear-btn's auto margin is what holds the two apart. */
  #composer-draft {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 0 0 100%;
  }
  #composer-draft #clear-btn { display: inline-flex; margin-right: auto; }
}

/* Reading-mode: the panel is hidden by default and shown only when opened
   (input-focused, set by the ✎ FAB / focus). There is no auto-hide on blur;
   input-focused is a manual open/close flag — ✎ raises the keyboard with the
   panel, ▾ takes both away.

   Which screens ≤768px actually is, since "phones and tablets" read as all of
   both and is neither: every phone in portrait, an iPad mini in portrait (744),
   and any iPad in Split View or Slide Over, where the app's share of the width
   drops well under the breakpoint. NOT a full-screen iPad in portrait (820 and
   up), nor any iPad in landscape, nor a phone in landscape — those get the
   desktop layout, panel and status rows always up. */
@media (max-width: 768px) {
  /* The CHIP rows are what reading-mode still takes: wake-ups, services and the
     queue are three rows of ~47px that describe work rather than carry it, and
     they are the pixels this mode was invented for. (The other three in this
     list are hidden on a phone unconditionally further down; they are named
     here only so the rule reads as one set.)
     #prompt-row is deliberately NOT in the list, though it is a chip row like
     the others: its chips are things to SEND, and reaching for one is the whole
     use — having to raise the keyboard first would be the opposite of the row's
     point. It starts away (07-prompts.css), so it costs 57px of chat in reading
     mode (measured at 390) only for someone who put it up, and the same mark
     takes it away again. The mark stays on screen in both states, so the setting
     always has something to act on. */
  body:not(.input-focused):not(.controls-active) #prompt-buttons-bar,
  body:not(.input-focused):not(.controls-active) #pins-bar,
  body:not(.input-focused):not(.controls-active) #wakeups-bar,
  body:not(.input-focused):not(.controls-active) #services-bar,
  body:not(.input-focused):not(.controls-active) #queue-bar,
  body:not(.input-focused):not(.controls-active) #inbox-bar {
    display: none !important;
  }

  /* #owed-bar is not in that list either, and for a blunter reason than jobs':
     it does not exist on a phone at all (see the unconditional hide further
     down). Reading mode is a phone rule, so a reading-mode line for that row
     could never apply — and a rule that cannot fire still reads as a claim that
     the row takes part.

     #jobs-bar is not in that list either, and it is not in reading mode's gift
     any more: ON A PHONE THE ROW IS GONE ALTOGETHER, in a session and out of it.
     What follows is why it used to be the one row reading mode spared, and why
     that stopped being the right answer.

     Every row in the list above describes something waiting for YOU — a pin you
     filed, a note you wrote, a message you queued, a service you started and can
     see the page of. Reading mode takes them because you already know they are
     there and can come back. A running job was the opposite: the machine doing
     something unattended, for possibly hours, THAT NOTHING ELSE ON A PHONE WOULD
     TELL YOU ABOUT — the dashboard a screen away, the chat silent. That last
     clause was the whole argument, and it is what expired.

     Two marks in #composer-sections say it now, both nearer the thumb than this
     row and both surviving reading mode, which is more than the row managed:
       - the jobs mark carries the count of what is RUNNING and opens on the full
         list (syncComposerSections). It was an inventory — measured at 390 with
         one job running, one finished-and-undelivered and one long delivered, it
         read "3" — and a badge that says the same thing whether or not anything
         is working could not have replaced this row. It counts the running ones
         now, which is the half of this comment that had to become true before
         the row could go.
       - the owed mark carries a job whose RESULT has not been handed over, which
         is the other thing this row was thinned down to keep (the .job-notice
         arm of the rule by #jobs-bar above). Measured in the same session: count
         1, `section-attention`, "Queued for this session: 1 parked — a parked one
         is not coming on its own", and the job itself on the list behind it.
         That is a sharper signal than a card, not a poorer one: the row only
         ever said "something is here", and this says what and how bad.

     So the row is not a signal any more, it is a second door to one room, and
     this file argues elsewhere that a phone does not need two. The owner, on a
     phone, looking at the card above the message box, said it was not needed
     there.

     What it gives back goes to the TRANSCRIPT, which is the thing a phone is
     shortest of. Measured at 390 and again at 320, one job running and one
     undelivered: the row was 133px sitting directly above the box, #input-area
     does not move (y=677.4 either way), and #messages goes 503.4 -> 636.4px.
     A quarter more chat, on a screen 844 tall. */
  #jobs-bar { display: none !important; }

  /* While the caret is in the box AND a keyboard is measurably taking the room,
     the chat and the box are what is left of the screen — three rows of settings
     are not. They come back on blur, or as soon as the keyboard goes.

     Both halves, because giving back the keyboard's room is the whole reason:
     a tablet with a hardware keyboard holds the caret for the entire session
     and shrinks nothing, and this used to cost it the status rows for just as
     long. See composerTyping in app.js.

     This can never coincide with the parked composer above it: that one needs
     the box NOT to hold focus and this one needs it to, so a screen with
     neither the composer nor the status rows cannot be reached. */
  body.input-typing #status-bar { display: none; }

  /* The two ways the box is off the screen, and they look the same. Either the
     panel was never opened, or it is open with nothing able to type into it
     (composerParked in app.js — the keyboard measurably gone while the caret is
     elsewhere). The box goes, and with it every control whose subject is the
     text inside it: ➤ sends that text, 📌 files it away, 📝 hands it to the
     running agent. All three act on something you cannot see, so all three go.
     What stays is the row itself and the four section buttons, which open lists
     and have nothing to do with the draft.
     Taking the whole #input-area is what shipped first, and it took the buttons
     with it: losing focus is exactly what sends the keyboard away, so tapping ⋯
     dismissed the row that ⋯ lives in, ~300ms after the tap. The bar's ⌨ brings
     the box and all three back in one tap. */
  body.input-parked #message-input,
  body:not(.input-focused):not(.controls-active) #message-input {
    display: none;
  }
  /* With the box gone the row is one line of controls, so it stands as tall as
     the other one-line row of controls — the bar beneath it. Measured, not
     assumed: the bar is 45px outside (44 of section plus its 1px border-top) and
     the row was 57 (the same 44 of button, its own 1px border-top, and 6+6 of
     padding). Dropping the vertical padding leaves 44+1 = 45 either way, and the
     two match on both readings — 44 of content against 44, 45 of box against 45.
     Horizontal padding is untouched: it is what holds the row off the edges.
     The 44px targets do NOT shrink. That is the whole reason this works — the
     height being removed is space around the buttons, not the buttons.

     BOTH ways the box can be gone, which is the same pair of selectors the box
     and its three buttons are hidden by, four rules up. Written for the parked
     state alone it would have made the two disagree: closing the panel would
     leave a 57px row and letting it park would give a 45px one, with the same
     controls in both — which is the exact thing c5c10f4 exists to prevent
     ("a hidden box looks the same however it got hidden"). The height belongs to
     the absent box, not to the route by which it went. */
  body.input-parked #input-area,
  body:not(.input-focused):not(.controls-active) #input-area {
    padding-top: 0;
    padding-bottom: 0;
  }
  /* GONE, not merely invisible — the whole line goes with them. They used to be
     `visibility: hidden`, which kept their columns so that collapsing them could
     not slide ➤ under a thumb reaching for 📝. That argument was about a line
     they SHARED with the section buttons: hiding them there would have moved the
     sections sideways. They have a line to themselves now, so there is nothing
     beside them to slide, and what `visibility` leaves instead is a 44px band of
     nothing between the chat and the sections — an empty row, in the state where
     the point of the row below is to be the only thing there. */
  body.input-parked #composer-draft,
  body:not(.input-focused):not(.controls-active) #composer-draft {
    display: none;
  }
}

/* The bottom bar (phones and tablets, ≤768px). It replaces four mobile-only
   controls that each did one half of what a section here does: the floating ✎
   and ▾ that opened and closed the input panel, and the two ways in to the
   dashboard (a floating square and one tucked inside the input row).
   
   NOT position:fixed. iOS shrinks the VISUAL viewport when the keyboard comes
   up while leaving the layout viewport alone, so a fixed bottom bar ends up
   underneath the keyboard. As the last row of the chat column it is carried by
   the layout instead, and it shows in both panel states rather than only while
   the panel is away, which is what the FABs did. */
#mobile-bar { display: none; }
@media (max-width: 768px) {
  /* The bar, and the sheet's copy of it — ONE definition for both. They are the
     same object in the interface: a strip along the bottom edge, 44px of
     fingertip per section, clear of the home indicator. Written twice they drift
     immediately, and did: the sheet's came out 45px against the bar's 57,
     because only one of them had the 12px floor below.
     Why a copy exists at all: a showModal() dialog is in the top layer, so the
     real bar underneath cannot be tapped (measured — the point where a section
     sits hits the sheet's own button). The sheet has to carry one of its own;
     what it must not do is invent one. */
  #mobile-bar,
  .dialog-page .sheet-exit {
    display: flex;
    flex-shrink: 0;
    background: var(--bg-surface);
    border-top: 1px solid var(--border);
    /* The home indicator overlays the bottom edge of the screen, and a section
       flush with that edge takes the swipe instead of the tap.
       A FLOOR, not just the inset. `env(safe-area-inset-bottom)` only resolves
       to anything with `viewport-fit=cover` in the viewport meta, which this app
       does not set — so on the iPhone this line has been asking for 0px all
       along, which is exactly the reported symptom. 12px is the smallest strip
       that visibly separates the marks from the edge; where the inset IS
       reported (or one day is), the larger of the two wins. */
    padding-bottom: max(env(safe-area-inset-bottom, 0px), 12px);
  }
  .mobile-bar-section,
  .dialog-page .sheet-exit-btn {
    /* An equal share each, whatever the count — a third section is one line of
       markup and nothing here. */
    flex: 1 1 0;
    min-width: 0;
    /* Full height of the bar, and never below a fingertip. */
    min-height: 44px;
    align-self: stretch;
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    color: var(--text);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  /* Nothing in this bar is a word any more, so nothing needs a word's width.
     .mobile-bar-narrow used to pin three sections to a 44px square, and its
     whole argument was that the sections beside them were words that needed the
     room ("taking 44 leaves the words MORE than they had"). With the words gone
     the argument goes with them, and what is left is an asymmetry with no
     reason: four sections at 65px next to three at 44. Every section is an equal
     share again — which is also the largest target each can have. */
  /* Icons in the bar get a size of their own rather than following whatever
     font-size the section happens to inherit — a section that holds a mark and
     no words has no reason to be sized by type. 20px is the composer row's,
     which is the other place this set is read at a glance. */
  .mobile-bar-section .icon,
  .dialog-page .sheet-exit-btn .icon { width: 20px; height: 20px; }
  /* The one word in the bar — #mobile-bar-lead, and see the markup for why that
     section, and only it, says a name instead of drawing a mark. Mono because
     the name is a token the agent types, not prose; 14px because that is what
     the composer's own chips use, and five characters of it measure 42 of the
     section's 78 at 390. Not sized by the section's 20px, which exists for
     icons. */
  .mobile-bar-section .bar-word {
    font-family: var(--mono);
    font-size: 14px;
    line-height: 1;
    white-space: nowrap;
  }
  /* How many are in there, without opening it — the same badge, in the same
     place, as the composer's section row. */
  .mobile-bar-section[data-count] { position: relative; }
  .mobile-bar-section[data-count]::after {
    content: attr(data-count);
    position: absolute;
    top: 4px;
    right: 4px;
    min-width: 14px;
    height: 14px;
    padding: 0 3px;
    box-sizing: border-box;
    border-radius: 7px;
    background: var(--accent);
    color: var(--bg);
    font-size: 9px;
    line-height: 14px;
    font-weight: 600;
    text-align: center;
  }
  .mobile-bar-section:not(:disabled):active,
  .dialog-page .sheet-exit-btn:active { background: var(--bg-hover); }
  /* On, in the colour the status bar's toggles use for the same word — the
     keyboard glyph is fixed now, so the state has to be carried somewhere. */
  .mobile-bar-section.on { color: var(--green); }
  /* LOOK AT THIS — the same --orange, and the same class name, the composer
     row's marks wear for a parked delivery. One word for "something here needs a
     person", said the same way wherever it is said.
     The BADGE takes it too, and that is the point rather than decoration: the
     only thing this state means is that the number in the badge has stopped
     being true, so leaving it in the ordinary accent would be the alarm and the
     thing it is about disagreeing on the same button. See syncMobileBar. */
  .mobile-bar-section.section-attention { color: var(--orange); }
  .mobile-bar-section.section-attention[data-count]::after { background: var(--orange); }
  /* Dead: the section holds its column and does nothing, which is what the two
     container sections do on the dashboard when there is nothing in them.
     Dimming the live colour rather than swapping in --text-muted, because the
     distance from "live" then reads the same in every theme — nord's muted sits
     almost on top of its text (7.45 vs 8.73 against the bar), so the swap alone
     looked on there. At .45 the live mark is 2.2-6x the dead one everywhere. */
  .mobile-bar-section:disabled { opacity: 0.45; cursor: default; }
  /* An empty cell: it holds a column so the sections either side of it don't
     slide, and does nothing else. `visibility: hidden` keeps the box and takes
     the element out of both the tab order and hit testing. */
  .mobile-bar-section.blank { visibility: hidden; }
  /* A hairline between sections, so they read as separate targets. */
  .mobile-bar-section + .mobile-bar-section { border-left: 1px solid var(--border); }
}


/* An icon from the sprite at the top of index.html. Sized in `em` on purpose:
   every place that used to hold a glyph already sets a font-size for it, so an
   icon drops into that slot at the size the glyph had and nothing around it has
   to be re-measured. `currentColor` is inherited by the sprite's stroke, so the
   colour rules that were written for text keep applying unchanged — which is
   the whole reason not to use emoji here. */
.icon {
  width: 1em;
  height: 1em;
  display: inline-block;
  vertical-align: -0.125em;
  flex-shrink: 0;
  /* The sprite's symbols carry shape and nothing else; how they are painted is
     set HERE, on the element that uses them, because that is the only place it
     can be. <use> clones a symbol into this svg's shadow tree, so it inherits
     from this rule — presentation attributes on the sprite's own <svg> are not
     in that chain and reach nothing at all. */
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* The status bar's size, with a heavier stroke — PARTLY heavier, on purpose.
   The same icon puts a different weight on screen at each size: stroke 2 on a
   24 grid is 1.67px at 20px and 1.08px at 13px, so the status bar would read
   thinner than the composer row from the same drawing. Matching it exactly
   wants 3.08, and that is where the cheap fix stops being one: a stroke that
   heavy on a grid whose DETAIL didn't simplify — r2.5 circles, 2px gaps —
   closes the gaps and turns small icons into blobs. Real sets answer this by
   drawing a second, simpler set for small sizes. 2.6 gives 1.41px: most of the
   way back, and still open. */
.icon-sm {
  width: 13px;
  height: 13px;
  stroke-width: 2.6;
}
/* The disclosure size. 10px is what the triangles it replaces were set in, and
   at that size a stroke of 2 renders 0.83px — a grey hair. 3.4 puts it at
   1.42px, which is where .icon-sm lands too, so a chevron and a 13px mark are
   the same weight on screen despite being different numbers in the file. */
.icon-xs {
  width: 10px;
  height: 10px;
  stroke-width: 3.4;
}
/* The same chevron, turned. Up and down are not two drawings. */
.icon.up { transform: rotate(-90deg); }
.icon.down { transform: rotate(90deg); }

/* Buttons whose whole content is one mark. A glyph was centred by its own line
   box, and every one of these sat at a slightly different height because every
   glyph has different metrics — measured, the file browser's four came out 22,
   22, 20 and 28px tall. A mark has no metrics to inherit, so the box has to do
   the centring. */
#fb-upload,
#fb-refresh,
#fb-copy,
#fb-download,
#update-banner-dismiss,
#fb-tree-hide {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 22px;
}

/* The ROW is phone-only: on a desktop the prompt-buttons row is right there
   above the box, and that is where the one mark a desktop needs — jobs — is
   drawn instead (see #prompt-buttons-bar).
   The DRESSING below is not phone-only, which is why it sits out here: the jobs
   button in the prompt row is the same button wearing the same class, so there
   is one description of what a section mark looks like rather than two. */
#composer-sections { display: none; }
.composer-section {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  color: var(--text-muted);
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
  /* 16px in 6px of padding is a 28px button and a 224px row of eight: a strip of
     marks under the box, not a second toolbar competing with the prompt buttons
     above it. A phone takes the same buttons to 20/8, because there they are
     fingertip targets rather than things to click. */
  font-size: 16px;
  padding: 0 6px;
  /* The glyph alone is 16 tall, which is a strip to miss rather than a button to
     press — 24 is what a button in a desktop toolbar is, and it costs the
     composer 8px. `align-items: center` above puts the mark in the middle of it.
     A coarse pointer overrides this with 44 further down, as it always did. */
  min-height: 24px;
}
.composer-section:disabled { opacity: 0.4; cursor: default; }
/* How many are in there, without opening it. Out on the corner, past the box: a
   desktop mark is a 16px glyph in a 28-30px box, and the phone's 14px badge one
   pixel in from that corner sat on the glyph — measured at 1280, 42% of it for
   "7" and 52% for "12". Here the marks stand 8px apart in a row that clips
   nothing (#prompt-buttons-bar; the first ancestor with overflow is #chat-area,
   which holds the whole row), so the corner is free to use. The phone puts it
   back inside the box, below. */
.composer-section[data-count]::after {
  content: attr(data-count);
  position: absolute;
  top: -3px;
  right: -3px;
  min-width: 11px;
  height: 11px;
  padding: 0 2px;
  box-sizing: border-box;
  border-radius: 5.5px;
  background: var(--accent);
  color: var(--bg);
  font-size: 8px;
  font-weight: 600;
  line-height: 11px;
  text-align: center;
}
@media (max-width: 768px) {
  /* No gap inside the group: the glyphs are ~20px in a 44px box, so closed up
     they still stand ~24px apart — this buys the target width out of empty
     space, not out of legibility, and it is what fits six marks into the 300px
     a 320 screen has inside the padding.
     Six, not all of them: NINE are 396 (ten with a queue up — measured at 390
     and 320 with a service, a job, two subagents and a waiting report, the row's
     busiest honest state). So the group scrolls sideways instead, exactly as
     .status-right does one screen down and for the same reason: wrapping it onto
     another row would eat chat height, and it would do so as counts change,
     moving the row under a thumb while the agent works. Every target keeps its
     44px, nothing is hidden, and the row is the same height at every width. That
     costs a swipe to reach the last marks: 96px of it at 320, and 26 at 390
     since the subagents mark moved in here from the bottom bar — the quiet case
     a session is usually in is six marks at 264, which fits either screen
     whole. */
  #composer-sections {
    display: flex;
    /* No gap between the marks — the padding inside each is the spacing, and
       that is what buys the 44px targets out of empty space (above). */
    gap: 0;
    /* LAST in the row and last in the markup, on the line under the draft
       buttons — which it never shares now, because those take a whole line (see
       #input-area above). `flex: 0 0 auto` asks for exactly its own width, so the
       group starts at the left of its line and stops where its marks do.
       `max-width: 100%` is what makes a long row scroll instead of overflowing:
       at 320 eight marks are 352 wide in 300 of container, and this caps the box
       so overflow-x has something to do. (`flex: 1 1 44px`, once, grew into
       whatever a shared line had left and gave eight sections 109px of a 390
       screen to scroll 352 through.) */
    flex: 0 0 auto;
    max-width: 100%;
    min-width: 0;
    /* Out past the row's own 10px, because these are 44px targets with a 20px
       mark centred in each: the first mark sat 32px from the screen edge while
       everything above it in the panel starts at 10. Four back puts the group
       where the eye already is. */
    margin-left: -4px;
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  #composer-sections::-webkit-scrollbar { display: none; }
  /* Bigger marks in more padding, which is the whole difference: a 20px glyph in
     a box wide enough that the coarse-pointer rule below has 44px to reach. */
  .composer-section { font-size: 20px; padding: 0 8px; }
  /* The count inside the box rather than hanging off its corner the way
     #note-btn's used to: here the boxes touch and the row clips its overflow, so
     a badge outside one would sit on top of its neighbour or lose its top. The
     glyph is 20px in a 44px box at 390, which leaves the badge room in the
     corner: measured, it covers 2-4% of the glyph. */
  .composer-section[data-count]::after {
    top: 1px;
    right: 1px;
    min-width: 14px;
    height: 14px;
    padding: 0 3px;
    border-radius: 7px;
    font-size: 9px;
    font-weight: inherit;
    line-height: 14px;
  }
}
/* The same fingertip rule its neighbours in this row follow. */
@media (pointer: coarse) {
  .composer-section { min-width: 44px; min-height: 44px; }
}

/* Pin / note rows. The text is the point of these, so it gets the full width and
   as many lines as it needs, and the buttons go underneath — beside a block that
   grows, a button either stretches with it or floats in the middle of it. */
.pd-item {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 12px;
  margin-bottom: 8px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.pd-item-head {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: var(--text-muted);
}
.pd-item-icon { font-size: 13px; }
.pd-item-text {
  font-size: 13px;
  color: var(--text);
  /* Wrapping is the whole reason these moved here. `anywhere` covers a single
     unbroken token — a path or a URL — which would otherwise push the dialog
     wider than the phone. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.pd-item-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.pd-item-btn {
  min-height: 44px;
  padding: 8px 14px;
  background: var(--bg-surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: 13px;
  cursor: pointer;
}
.pd-item-btn:active { background: var(--bg-hover); }
.pd-item-btn.danger { color: var(--text-muted); }

/* --- Prompts dialog (the phone's lists: this session's own, and the git
   commands) ---
   The prompt library left this dialog when #prompt-row arrived (07-prompts.css),
   and the row-and-two-buttons list it used to draw left with it — what remains
   are the faces whose lists have nowhere else to stand on a phone. */
.prompts-dialog-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 16px;
}
.prompts-dialog-preset {
  padding: 10px 12px;
  min-height: 44px;
  text-align: left;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: 14px;
  cursor: pointer;
}
.prompts-dialog-preset:active {
  background: var(--bg-hover);
}
.prompts-dialog-empty {
  color: var(--text-muted);
  font-size: 13px;
}

/* The button that SENDS, wherever it is: the composer's, and the one on a pin
   chip that sends that pin now. Written once, because the two of them being the
   same shape is the whole point — a person should recognise "this sends" by the
   look of it, not by working out which row they are in.
   Round, and it stays round. It is the only filled control in the composer row,
   and the shape is doing hierarchy: two outlined boxes for what you MIGHT do
   with the draft, one filled circle for what you came here to do. Squared off,
   it reads as a third sibling of the boxes beside it.
   Only the diameter differs between the two, so that is the only thing either
   of them says — the glyph follows it, at half the circle. */
.send-circle {
  width: var(--send-size, 36px);
  height: var(--send-size, 36px);
  padding: 0;
  border: none;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  font-size: calc(var(--send-size, 36px) / 2);
  line-height: 1;
  background: var(--accent);
  color: var(--bg);
  flex-shrink: 0;
  transition: background 0.15s;
}
.send-circle:hover:not(:disabled) { background: var(--accent-hover); }
.send-circle:disabled { opacity: 0.4; cursor: not-allowed; }

#abort-btn {
  padding: 4px 12px;
  border: none;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 13px;
  font-weight: 500;
  background: var(--red);
  color: white;
  white-space: nowrap;
  transition: background 0.15s;
}
/* Every red-filled button darkens by the same fifth on hover, and none of the
   three names a colour. #f44336 did, which made the hover a fixed shade with no
   relation to the theme's red: it was barely a change at all in the default,
   monokai and gruvbox (1.03-1.07 from the base), and in the light theme it
   UNDID that theme's red — 6.47 white-on-red at rest, 3.68 the moment the
   cursor arrived, on a button whose whole job is to be sure about.

   Mixed toward BLACK rather than toward var(--text), which is what the accent's
   hover does. The accent's asymmetry is right there because what sits on it is
   var(--bg), so "more" means whichever way the theme leans. What sits on THESE
   is a literal white, and white wants a darker fill in every theme — mixing
   toward var(--text) would lighten the red in the six dark themes and drop the
   contrast below the resting button in five of the eight (default 3.49 -> 3.10,
   dracula 3.14 -> 2.76). `black` here is a direction, the way `transparent` is
   in the color-mixes above it, not a colour out of the palette.

   A fifth, because that is the step the accent's hover already makes (1.46) and
   because at 20% every theme's hover clears 4.5 even where its resting red does
   not: 4.69 in dracula, 5.09 in gruvbox, 5.16 in the default. */
#abort-btn:hover { background: color-mix(in srgb, var(--red), black 20%); }

/* Esc stopped the turn (esc-stop.js): the button that WOULD have been pressed
   says so, because a key has no cursor behind it and nothing else on screen
   changes until the relay answers. The same darkening the hover makes, held for
   a moment and let go — a press, drawn. No outline and no transform: this button
   sits in a header row beside the connection label, and a shape that grows would
   push it. */
#abort-btn.stop-flash { animation: stop-flash 0.45s ease-out; }
@keyframes stop-flash {
  0%, 40% { background: color-mix(in srgb, var(--red), white 35%); }
  100% { background: var(--red); }
}
/* Reduced motion keeps the flash and drops the fade: it is the only feedback
   this act has, so it holds still for its moment instead. */
@media (prefers-reduced-motion: reduce) {
  #abort-btn.stop-flash {
    animation: none;
    background: color-mix(in srgb, var(--red), white 35%);
  }
}

