/* --- Update banner (in header, only when server build_id has bumped) --- */
/* Shown while the page is fetching what it missed. A row of the header, like
   the update banner it sits above, so it takes space from nothing — the whole
   point of the header being a column of rows. */
/* Out of flow, and that is the whole point. This used to be a band in the
   header, which meant every catch-up — a glance away and back, routine, several
   times a minute on a phone — pushed the entire app down by its height and
   pulled it back up again a moment later. A notice about staleness is not worth
   one pixel of movement in what you are reading, let alone twenty.
   So: a pill floating under the header, absolute inside it, laid OVER the top
   of the chat rather than above it. It costs no layout, so it can appear and
   vanish as often as it likes. pointer-events: none because it covers a message
   row and must never eat the click meant for it. */
#sync-banner {
  position: absolute;
  top: calc(100% + 6px);
  /* Centred, unlike the old band's left edge: a thing that floats over content
     has no column to line up with, and the middle is where a transient notice
     is looked for. */
  left: 50%;
  transform: translateX(-50%);
  max-width: min(92%, 420px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 4px 12px;
  border-radius: 999px;
  border: 1px solid var(--border);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.28);
  background: var(--bg-surface2, var(--bg-hover));
  /* Full text colour, not muted. As a band it sat in the chrome, where muted is
     right; floating over the chat it has to out-read the message under it, and
     muted measured 2.2:1 against the pill's own fill. */
  color: var(--text);
  font-size: 12px;
  font-weight: 600;
  pointer-events: none;
}
#sync-banner.hidden { display: none; }
/* It appears out of nothing now that it displaces nothing, so it gets a short
   fade to say where it came from. Display none -> block restarts the animation,
   which is why this is an animation and not a transition. */
@keyframes sync-pill-in {
  from { opacity: 0; transform: translate(-50%, -4px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
#sync-banner:not(.hidden) { animation: sync-pill-in 140ms ease-out; }
@media (prefers-reduced-motion: reduce) {
  #sync-banner:not(.hidden) { animation: none; }
}
/* Catching up is routine; failing to is not, and the two must not look alike —
   the whole value of the notice is that the user can tell whether what they are
   reading has been brought up to date. */
#sync-banner.stalled {
  background: var(--orange);
  color: var(--bg);
  border-color: var(--orange);
}

/* A full-width band under the controls, at every width. One behaviour instead
   of an inline chip that becomes a band below some breakpoint: it competes with
   nothing, so there is no width at which something has to give way to it. */
#update-banner {
  display: flex;
  align-items: center;
  /* The message and the thing to do about it start at the left edge, where
     reading starts; ✕ is pushed to the other end (see its margin below). */
  justify-content: flex-start;
  gap: 8px;
  padding: 4px 16px;
  background: var(--orange);
  color: var(--bg);
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}
/* The text is the only part that may give way. `nowrap` above keeps the banner
   one line, and without this the line simply grew past both edges of the screen:
   a longer message than the usual one pushed ✕ to x=515 on a 320px phone —
   off the screen, so the notice could not be dismissed at all. Shrink, not grow,
   so a short message keeps Reload next to it rather than half a row away. */
#update-banner > span {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
#update-banner-reload {
  background: var(--bg);
  color: var(--orange);
  border: none;
  padding: 3px 10px;
  border-radius: 3px;
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
}
#update-banner-reload:hover { background: var(--bg-hover); }
#update-banner-reload { flex-shrink: 0; }
#update-banner-dismiss {
  /* Held against the far edge rather than left next to Reload. On a phone both
     are 44px targets sitting side by side, and the two do opposite things —
     one reloads the page out from under you, the other only hides a line of
     text. The distance is the guard against hitting the wrong one, and it is
     what this banner's shape buys by aligning left in the first place. */
  margin-left: auto;
  flex-shrink: 0;
  background: none;
  border: none;
  color: var(--bg);
  cursor: pointer;
  font-size: 12px;
  opacity: 0.7;
  padding: 0 2px;
}
#update-banner-dismiss:hover { opacity: 1; }
/* A fingertip needs the same 44px the quote button gets. These are 18px tall by
   the type inside them, which is a miss on a phone — and this banner's whole
   purpose is to be pressed. */
@media (pointer: coarse) {
  #update-banner-reload { min-height: 44px; padding: 3px 16px; }
  #update-banner-dismiss { min-height: 44px; min-width: 44px; }
}
@media (max-width: 768px) {
  #status-bar { flex-direction: column; align-items: stretch; padding: 2px 10px; gap: 2px; }
  /* Agent · model · effort last, so the settings you reach for sit closest to
     the thumb. `order` rather than a move in the markup: one document serves
     both widths, and moving the element would reorder the desktop row too. */
  .status-middle { order: 1; }

  /* Tapping the panel's own background folds it down to that one row — the
     handler is setStatusBarCollapsed in app.js, and it sets the class at every
     width. The breakpoint is only here, because only here is there anything to
     fold: on a desktop the panel is a single row and the class means nothing.
     Nothing is added to say the panel folds — a chevron would cost the row it
     is trying to save, and what's left reading `claude · opus · high` is a
     line worth looking at rather than a stub of a hidden one.

     Plain `display: none`, not `!important`: the rules that take the WHOLE
     panel away (body:not(.in-chat), body.input-typing) hide the parent, so
     they keep winning over this whatever it says. */
  #status-bar.status-collapsed .status-left,
  #status-bar.status-collapsed .status-right { display: none; }

  /* Two rows are gone from the phone here: the pins and the inbox notes. Both
     are in #prompts-dialog now, one tap away in the composer row, and the two
     chip rows gain by the move — their text was one clipped line with the rest
     in a `title`, which a phone cannot show at all. The prompt buttons went the
     same way once and came back as a row of their own: a phone draws them in
     #prompt-row, which its own mark switches (07-prompts.css), so what this
     rule still hides is the desktop bar's copy of them.
     `#wakeups-bar` deliberately stays: it wasn't part of this. Unconditional,
     unlike the reading-mode rules above — they stay away whether or not the
     panel is open. */
  #prompt-buttons-bar, #pins-bar, #inbox-bar,
  /* #owed-bar is a DESKTOP row only — its switch lives in #prompt-buttons-bar,
     which is itself gone here, and the phone's copy of the mark opens the
     sheet instead. Said as a rule rather than left to the switch never being
     flipped: a body class set at a desk must not reach across and put a row
     on a phone that has no mark to take it away again. */
  #owed-bar { display: none !important; }
  /* The dashboard's services and jobs blocks, for the same reason those three
     rows went: they are lists to open, not things to scroll past on the way to
     the session you came for. The bar's two sections hold them here — counting
     every session's rather than the open one's, since on this screen there is
     no open one — and the sheet they raise is the same cards, grouped and
     labelled the same way the blocks were. */
  #dashboard-services, #dashboard-jobs, #dashboard-sessions, #dashboard-turns,
  #dashboard-rail { display: none !important; }
  .status-left, .status-middle, .status-right {
    width: 100%;
    flex: 0 0 auto;
    margin-left: 0;
    /* The status-right row holds several toggles (ssh / gh / glab / subagents /
       keepcontainer / mode / bell) which together overflow a phone
       screen. flex-wrap would stack them vertically and eat chat space;
       horizontal scroll keeps the row to one line and lets the user
       swipe to reach the off-screen toggles. */
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .status-left::-webkit-scrollbar,
  .status-middle::-webkit-scrollbar,
  .status-right::-webkit-scrollbar { display: none; }
  /* Centred, but only while it fits. These rows scroll sideways (above), and
     `center` on a scroll container that overflows pushes the overrun off BOTH
     ends — the part before the start has no scroll position to reach it, so the
     first toggles simply become unreachable. `safe` is the keyword for exactly
     that: centre when there is room, fall back to the start when there isn't.

     Written twice on purpose. A browser that doesn't know `safe` drops the
     second declaration and keeps the first, which is what each row says today —
     so nobody ends up worse off than before this rule, and the newer ones get
     the centring without the trap. */
  .status-left {
    justify-content: flex-start;
    justify-content: safe center;
  }
  .status-middle {
    justify-content: center;
    justify-content: safe center;
  }
  .status-right {
    justify-content: flex-start;
    justify-content: safe center;
  }

  /* The session name is already the header title, two rows up on a screen this
     small — writing it again down here spends the row on the one thing the user
     is looking straight at. The rest of the run is untouched: the cwd, the
     worktree tail, the branch and its pills are only ever written here.

     `display: contents` on the wrapper rather than `display: none`: it is a flex
     item, so hiding only the name inside it would leave a zero-width item and
     the row's 12px gap sitting in front of the cwd. Dropping its box takes the
     gap with it — and, unlike hiding the whole element, it lets what ISN'T the
     name stay. For a subagent's subchat that is the @nick and whose subagent it
     is, which has nowhere else to go: the header carries the name and the agent
     badge and nothing more, and the row it sits in is the one we just stopped
     squeezing. This row can afford it — it scrolls sideways. */
  #status-session { display: contents; }
  .status-session-name { display: none; }
  /* Dropping the box promotes what's left to flex items of the row, and a flex
     item that can't shrink pushes the row past the screen — the whole hint used
     to ride inside one item that ellipsised. It ellipsises on its own now, after
     the cwd has given way, because whose subagent this is answers a question
     the user cannot answer any other way here while the path is also spelled out
     in the header of every `cd`. The @nick never gives way at all — it is the
     one word that names this chat. */
  .status-subagent-of {
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
    flex-shrink: 0.25;
  }
}
/* The rate limits, on a phone only — the header's pills say the same thing on a
   wide screen and this one is hidden there (see the pair of media queries beside
   #global-limits). Of the two things in the middle column it is the one that
   yields when the column is tight, because `#status-run` beside it is the only
   place the model and its knobs are written. So it ellipsises and #status-run
   keeps its width. */
#status-limits {
  font-family: var(--mono);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
#status-limits.warn { color: var(--orange); }
#status-limits.crit { color: var(--red); }
/* agent · model · effort · fast, then the context ring. The separator is a
   ::before on the part to its right, so hiding a part (fast on a model without
   it, effort where the level is baked into the model id) removes its separator
   too — no dangling dot. */
#status-run {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  flex-shrink: 0;
}
/* The dot is punctuation, not part of the control it precedes. It lives inside
   that element, so without these it would inherit its colour, its hover and its
   underline — the dot before the model went accent on hover, the one before
   `fast` went green when fast was on. Fixed colour, no pointer target, and
   inline-block so the parent's underline isn't drawn through it. */
#status-run > span + span::before {
  content: "·";
  margin-right: 6px;
  display: inline-block;
  color: var(--text-muted);
  text-decoration: none;
  pointer-events: none;
}
#status-agent,
#status-model {
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-weight: 600;
}
/* The agent is a label — it names what you're talking to and can't be changed
   on a live session. Only the parts you can act on look actionable. */
#status-model {
  cursor: pointer;
  user-select: none;
  transition: color 0.15s;
}
#status-model:hover { color: var(--accent); }

/* Popup used by the status-bar pickers (model, effort) — each built by its own
   function in app.js; only the dressing is shared. Mirrors the dark popup menus
   (#preset-menu / .session-menu); opens ABOVE the status bar. */
.picker-menu {
  position: fixed;
  z-index: 200;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px;
  min-width: 200px;
  max-height: 320px;
  overflow-y: auto;
  box-shadow: 0 4px 14px rgba(0,0,0,0.4);
}
.picker-menu-item {
  display: flex;
  align-items: baseline;
  gap: 6px;
  width: 100%;
  text-align: left;
  background: transparent;
  color: var(--text);
  border: none;
  padding: 6px 8px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 13px;
  font-family: var(--mono);
  white-space: nowrap;
}
.picker-menu-item:hover { background: var(--bg-hover); }
.picker-menu-item.active { color: var(--accent); }
.picker-menu-check {
  display: inline-block;
  width: 12px;
  flex: 0 0 12px;
  color: var(--accent);
}
.picker-menu-label { flex: 0 0 auto; }
.picker-menu-id {
  color: var(--text-muted);
  font-size: 11px;
  margin-left: auto;
  padding-left: 12px;
}
/* Same typography as #status-agent, which it now sits beside: both are per-turn
   settings picked from a list. The pill border/padding are gone so the two read
   as siblings; the level keeps its colour, which is the one thing the chip says
   at a glance that the model chip doesn't. */
#status-effort {
  cursor: pointer;
  user-select: none;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-weight: 600;
  color: var(--text-muted);
  transition: color 0.15s;
}
#status-effort:hover { color: var(--accent); }
/* copilot alone offers "none". */
#status-effort.effort-none    { color: var(--text-muted); }
#status-effort.effort-minimal { color: var(--text-muted); }
#status-effort.effort-low    { color: var(--blue); }
#status-effort.effort-medium { color: var(--green); }
#status-effort.effort-high   { color: var(--yellow, #ffc107); }
#status-effort.effort-xhigh  { color: var(--orange); }
#status-effort.effort-max    { color: var(--red); }
#status-effort:hover.effort-none,
#status-effort:hover.effort-minimal,
#status-effort:hover.effort-low,
#status-effort:hover.effort-medium,
#status-effort:hover.effort-high,
#status-effort:hover.effort-xhigh,
#status-effort:hover.effort-max { color: var(--accent); }
/* Holds an inline SVG (see modeIcon in app.js). Deliberately NOT a flex box: a
   flex box takes its height from the icon (12px), which made this frame 18px
   against its neighbours' 22px. Left inline, the line box below sets the height
   exactly as it does for the text chips. The border keeps carrying the mode
   colour along with the stroke — on yolo that red outline is doing real work. */
/* A class, not an id, because the New Session dialog wears the same chip: what
   the user picks there is what the bar will show a second later, and a second
   set of rules for "how plan looks" is how two of them end up disagreeing. */
/* THE FRAME MEANS A SWITCH. Every chip in this app that flips something wears
   it — the proxy grants, the mode chip — and everything that merely opens a
   thing does not: the section marks are bare, and reading a row of them you can
   see which ones act on the screen in front of you before you press anything.
   The four section marks that put their row away are switches by that rule, so
   they wear the frame too (see .bar-toggle), in BOTH states — what the row is
   doing is said by the colour inside the frame, and a signal that disappears in
   the state a session opens in is not a signal.
   Written once here, so the three wearers cannot drift into three slightly
   different frames; each keeps its own colours, which is what the frame is
   there to carry. */
.proxy-toggle,
.mode-chip,
.composer-section.bar-toggle {
  border: 1px solid var(--border);
  border-radius: 4px;
}

/* inline-flex, so what is inside sits in the middle of the frame by geometry.
   As an inline box it relied on `vertical-align: middle`, which does not mean
   the middle of anything: it aligns the box's centre with the baseline plus half
   the parent's x-height, so where a 13px icon lands inside a 16px line depends
   on the FONT, and it landed low. */
.mode-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  line-height: 16px;
  padding: 2px 6px;
  color: var(--text-muted);
  transition: color 0.15s, background 0.15s, border-color 0.15s;
}
.mode-chip:hover { color: var(--text); border-color: var(--accent); }
/* What colour means on the chips in this row, which is now one system:
   --red is reserved for something having GONE WRONG — a dropped connection, an
   error dot, a failed job, a stopped service, deleted lines, an expired timer,
   every "remove" hover. yolo wore it, and yolo is the mode most sessions here
   run in, so the screen carried a permanent alarm that was usually the only red
   on it. An alarm that is always on stops being read as one and drags the real
   ones down with it. A mode of operation is not a fault, so it is gone.
   The GREEN FILL means one thing: a capability the agent does not have by
   default has been granted to it — gh, glab, subagents, ssh. Not "good",
   "granted"; ssh is the most dangerous thing in that list. yolo belongs there:
   it lets the agent change things without asking, which is the same kind of
   grant as reaching gh. So it wears the same rule, literally the same one —
   see .proxy-toggle.proxy-on, which this selector joins rather than copies.
   The ungranted state in that family is NEUTRAL rather than a colour of its
   own, which is why plan is now the plain muted chip. No mode loses its
   legibility for it: modeIcon (app.js) draws a DIFFERENT mark per mode — the
   pencil against the crossed pencil — so the colour was repeating the glyph
   rather than saying anything it didn't.
   `mode-auto` is the one chip left outside this system, and knowingly: it is a
   legacy mode nothing creates any more, and giving it a place in the scheme is a
   separate question from the one being answered here. */
.mode-chip.mode-auto { color: var(--orange); border-color: var(--orange); }
#status-bell {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  user-select: none;
  color: var(--text-muted);
  padding: 2px 6px;
  border-radius: 4px;
  border: 1px solid transparent;
  font-size: 13px;
  /* 16px like every other chip in this row, not `1` — at font-size 13 that read
     as 13 and made the bell the shortest thing in the bar. */
  line-height: 16px;
}
#status-bell:hover { color: var(--text); border-color: var(--border); }
#status-bell.bell-on { color: var(--accent); }
.proxy-toggle {
  /* Same reason as .mode-chip: one of these holds an icon (@) and the rest hold
     words, and both want the middle of the frame rather than a font's idea of it. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  user-select: none;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-weight: 600;
  font-size: 11px;
  /* Pinned in px, not left to `normal`: these chips carry glyphs at different
     font-sizes (@ is 13px) and one of them holds an icon, and the frame height
     must not follow whatever is inside.
     line-height alone does not deliver it for an icon-only chip — see the
     shared strut rule below .glyph-toggle. */
  line-height: 16px;
  padding: 2px 6px;
  color: var(--text-muted);
  transition: color 0.15s, background 0.15s, border-color 0.15s;
}
.proxy-toggle:hover { color: var(--text); border-color: var(--accent); }
/* GRANTED: a capability this agent does not have by default has been handed to
   it. gh, glab, subagents, ssh — and yolo, which is the same kind of grant in
   the mode chip's clothing (see .mode-chip above for the whole scheme). One
   declaration for all of them: two copies would drift the day --green moves.
   Deliberately after :hover, so an already-granted chip does not change colour
   under the pointer — the state is louder than the pointer, and that is how the
   proxy chips have always behaved. */
.proxy-toggle.proxy-on,
.mode-chip.mode-yolo {
  color: var(--bg);
  background: var(--green);
  border-color: var(--green);
}
.proxy-toggle.proxy-disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
/* The keep-container chip's state mark (paintKeepChip, messages.js — the whole
   argument for a dot and against green is written there).
   FILLED = live, the app's mark for something that is running; HOLLOW RING =
   clean, the same shape with nothing in it, borrowed exactly from `.auth-dot.none`
   (transparent, inset 1px ring) rather than invented here.
   `currentColor` in both states, so the dot is whatever the chip is — muted at
   rest, --text under the pointer — and cannot drift into the green that means a
   GRANT in this row.
   5px is the sidebar's read dot (.session-item .dot at scale(0.625)), which is
   what a small state mark measures everywhere in this app. Fixed, not relative
   to the font: the New Session dialog wears these same chips at font-size 15,
   and a dot that grew with the text would stop being the same mark.
   No vertical-align and no nudge: .proxy-toggle is an inline-flex with
   `align-items: center`, so the dot is centred against the line box by geometry. */
.keep-dot {
  width: 5px;
  height: 5px;
  margin-right: 4px;
  border-radius: 50%;
  flex-shrink: 0;
  background: currentColor;
}
[data-keep="clean"] .keep-dot {
  background: transparent;
  box-shadow: inset 0 0 0 1px currentColor;
}
/* An empty span in the status bar is collapsed by four rules in 10-status.css,
   so that a reading the relay has not sent yet costs no gap. This one is empty
   on purpose and for ever, and those rules name it as their exception — see the
   comment on `#status-bar span:empty` there. The dialog copies live outside the
   bar and were never reached by it. */
#status-gh, #status-glab {
  /* applied via class — keep id selectors empty for cascade clarity */
}
/* Worn with .proxy-toggle, which supplies the frame and the on/off colours; this
   only undoes the treatment meant for the lettered chips (gh, glab), since a
   glyph has no case to upper. A class for the same reason .mode-chip is one —
   the dialog's @ has to be the bar's @. */
.glyph-toggle {
  text-transform: none;
  letter-spacing: 0;
  font-size: 13px;
}

/* Why the chips in the status row are all the same height.
   Each of these sets a `line-height` to fix its frame — but line-height governs
   a LINE BOX, and a chip whose whole content is an <svg> has no line box for it
   to govern. Those chips took their height from the 13px icon instead and came
   out at 19px beside gh/glab's 22: three short, in a row that reads as a set.
   A zero-width space is the fix that needs no number. It is real text, so it
   builds a line box of exactly whatever line-height applies HERE — which is the
   part a hard-coded min-height gets wrong, since #session-toggles dresses these
   same chips at line-height 20px for the New Session dialog. Living in ::before
   also keeps it out of anything copied off the screen. */
.proxy-toggle::before,
.mode-chip::before,
#status-bell::before {
  content: "\200b";
}
/* Subagent identity in the chat status bar (next to the session name). */
.status-subagent-nick {
  color: var(--accent);
  font-weight: 600;
}
.status-subagent-of {
  color: var(--text-muted);
  font-size: 11px;
}
/* Beside the model and effort, and dressed like them — no pill. It IS a toggle,
   so unlike those two it carries its state in COLOUR: muted off, green on.
   Which is why hover must not touch colour — an accent-on-hover rule made on and
   off identical under the pointer, i.e. invisible exactly while you click it.
   Hover gets an underline instead, so colour only ever means state.
   The chip is hidden outright when the model can't take it (updateStatusBar),
   so there is no disabled state to style. */
#status-fast {
  cursor: pointer;
  user-select: none;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-weight: 600;
  color: var(--text-muted);
  transition: color 0.15s;
}
#status-fast:hover { text-decoration: underline; }
#status-fast.fast-on { color: var(--green); }
/* Context fill, trailing the run row and punctuated like the parts before it —
   it's the last reading in the same run, not a decoration hung off the end.
   Its dot needs no special case: empty innerHTML (no context yet, or codex just
   compacted) collapses the span via `#status-bar span:empty`, and a ::before
   goes with the element it lives on, so the row falls back to ending at `fast`
   with nothing dangling. It does need the margin zeroed, though: this span is a
   flex box, so its dot lands inside it as a flex item and takes the gap below
   as well as the margin every other dot carries — 12px of air where the rest of
   the row reads at 6. */
#status-run > #status-context::before { margin-right: 0; }
#status-context {
  font-family: var(--mono);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--accent);
}
#status-context.ctx-warn { color: var(--orange); }
#status-context.ctx-crit { color: var(--red); font-weight: 600; }
.ctx-ring { flex-shrink: 0; }

/* --- Global rate-limits indicator (header) --- */
#global-limits {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  color: var(--text-muted);
}
#global-limits:empty { display: none; }
.global-limit-pill {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  padding: 2px 6px;
  border-radius: 4px;
  border: 1px solid var(--border);
  background: var(--bg-surface);
  font-family: var(--mono);
  font-size: 10px;
  white-space: nowrap;
  cursor: default;
}
.global-limit-pill .gl-agent {
  text-transform: uppercase;
  font-weight: 600;
  letter-spacing: 0.5px;
  color: var(--text);
  font-size: 9px;
}
.global-limit-pill.gl-warn { border-color: var(--orange); color: var(--orange); }
.global-limit-pill.gl-warn .gl-agent { color: var(--orange); }
.global-limit-pill.gl-crit { border-color: var(--red); color: var(--red); }
.global-limit-pill.gl-crit .gl-agent { color: var(--red); }
/* WHERE THE RATE LIMITS ARE READ, and where they are deliberately not. The
   header pills carry them on a wide screen, which is the only place with room
   for a row of pills. They used to have a second home for narrow screens — the
   status bar's #status-limits, standing in for a header that has no room — and
   that home is gone: the owner asked (2026-09-18) that the phone's status row
   say `agent · model · effort` and nothing else, the readings included. The
   phone's reading is on the DASHBOARD instead, above the session filter
   (#dashboard-limits, 04-dashboard.css) — which is the same row, moved, not a
   third drawing of the store.

   #status-limits is still FILLED by updateStatusBar (public/js/status-bar.js) —
   the reading keeps flowing, its text and its warn/crit class are set as before,
   it is only never drawn. Deleting this one rule is the whole way back.

   One unconditional hide, not the pair of complementary queries this used to be
   (`max-width: 768px` for the header, its negation for the status bar): hidden
   at every width is what #status-limits now is, and writing that as two rules
   either side of a breakpoint would be two rules to keep in step describing a
   split that no longer exists.

   The header's own `max-width: 768px` hide went the same way, and for a better
   reason than tidiness: renderGlobalLimits now MOVES the row rather than draws
   it twice, so under 768px #global-limits is empty and the `:empty` rule above
   is what takes it off the screen. A media query saying the same thing would be
   a second answer to a question JS has already answered — and the one that
   would keep the header's copy alive, half-styled, if the two ever disagreed. */
#status-limits { display: none; }

.hidden { display: none !important; }

