/* ============================================================================
   NabGol — CHAT HEADER  (the top bar: robot identity + toolbar)
   ----------------------------------------------------------------------------
   Owns EXACTLY one thing: `.chat-header` and everything inside it. Does not
   touch `#messages` or `.chat-input` — those are chat-canvas.css's and (later)
   chat-composer.css's, by the same one-owner-per-region rule.

   Why this file exists: the message area now speaks one visual language
   (chat-canvas.css: hairline instead of frame, a three-level ink scale, green
   spent once). The header still spoke the old one — a semi-opaque colour bar
   with a blurred backdrop and a fully-saturated #10b981 toggle fill
   (`.autoplay-toggle.is-on`) — colour decisions from a different era, on the
   same bar the canvas now sits under.

   Scope decision: this file only retints EXISTING surfaces (background,
   border, color) with the same --nbgc-* tokens chat-canvas.css already
   defines. It does not touch:
     - size, padding, gap or breakpoints — the header's responsive layout
       (icon-only <-> label-reveal-on-hover, the 40px touch target inside the
       crowded mobile row, the desktop stage-width clamp) was already tuned
       across several passes (style-desktop.css "v13".."v24"); a pure skin
       pass has no reason to re-litigate hand-tuned geometry.
       AMENDED 2026-09-05 (step 5 of docs/CHAT_REDESIGN_MASTER_PLAN.md, §23):
       the DESKTOP header's geometry is now this file's, by assignment. The
       sentence above stayed true for four skin passes and stopped being true
       the moment the plan made "where does the header sit" the work itself;
       see section 9 at the end of this file for the whole of it. The phone
       header is untouched and the sentence still holds there.
     - `.avatar` — `body.tutor-composer-simple .chat-header .avatar:not(.has-img)`
       in chat-flagship-polish.css paints a per-subject hue ring + monogram
       (real product signal: which subject this tutor teaches), not a stray
       saturated gradient. Retinting it here would `!important`-clobber that
       feature on every tutor robot regardless of specificity, since a plain
       accent-wash rule has no way to defer to a more specific one once both
       carry `!important`. Left alone on purpose.
     - the danger-red on "clear chat", the inactive/red half of the presence
       pill: no --nbgc-* equivalent exists for either, and inventing one is
       against this row's own wall (docs/CHAT_CANVAS_HANDOFF.md).

   Load order: after chat-canvas.css, in both templates. `!important` mirrors
   chat-canvas.css's own reasoning: style-desktop.css/style-mobile.css claim
   these same properties with `!important` across several breakpoints, so a
   plain declaration here would lose to whichever of them loaded last.

   The light-theme duplicate block at the end exists because
   chat-flagship-polish.css already ships its OWN light-theme unification pass
   for `.chat-header` (`html[data-theme="light"] body:is(...) .chat-header {
   background: var(--bg-secondary) !important; ... }`, dated well before this
   file). That selector's specificity (0,3,2) or (0,4,2) beats this file's
   plain (0,2,1)/(0,3,1) rules even though this file loads later — load order
   only breaks TIES, and these are not ties. Every duplicate below repeats
   that exact selector shape so the tie is real and this file's later load
   order decides it. Found by measurement (test_chat_header_gate.js caught
   the light-mode header rendering fully opaque), not by inspection.
   ========================================================================== */

/* ---------------------------------------------------------------------------
   1) THE BAR ITSELF — hairline instead of a filled, blurred slab.
   --------------------------------------------------------------------------- */
body:is(.desktop-chat, .mobile-chat) .chat-header {
  /* One line, one token, owner's call: the whole chat page gets the canvas's
     face, so the robot's name in the header and the answer under it are set in
     the same voice. Defined once in chat-canvas.css; never respell it here. */
  font-family: var(--nbgc-font) !important;
  background: none !important;
  background-image: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  border-bottom: 1px solid var(--nbgc-hairline) !important;
  box-shadow: none !important;
}

/* ---------------------------------------------------------------------------
   2) IDENTITY TEXT — name and description. Ink hierarchy matches the canvas
   (ink-strong for the name, ink-soft for the description). Avatar excluded —
   see the file header.
   --------------------------------------------------------------------------- */
body:is(.desktop-chat, .mobile-chat) .chat-header .name {
  color: var(--nbgc-ink-strong) !important;
}
body:is(.desktop-chat, .mobile-chat) .chat-header .robot-desc,
body:is(.desktop-chat, .mobile-chat) .chat-header .info-text .robot-desc {
  color: var(--nbgc-ink-soft) !important;
}

/* ---------------------------------------------------------------------------
   3) PRESENCE — "active"/"inactive" is a real fact (docs/ROUTER.md's honesty
   rule), not decoration, so both states stay legible; only the accent side
   moves onto --nbgc-* tokens. The inactive/red pill (style-common.css) has no
   --nbgc-* equivalent and is left as-is on purpose.
   --------------------------------------------------------------------------- */
body:is(.desktop-chat, .mobile-chat) .chat-header .robot-status .status-dot {
  background: var(--nbgc-ink-faint) !important;
  box-shadow: none !important;
}
body:is(.desktop-chat, .mobile-chat) .chat-header .robot-status.active {
  color: var(--nbgc-accent-ink) !important;
  background: var(--nbgc-accent-wash) !important;
}
body:is(.desktop-chat, .mobile-chat) .chat-header .robot-status.active .status-dot {
  background: var(--nbgc-accent-ink) !important;
  box-shadow: none !important;
}
body.mobile-chat .chat-header .robot-status-line.active {
  color: var(--nbgc-accent-ink) !important;
}
body.mobile-chat .chat-header .robot-status-line.inactive {
  color: var(--nbgc-ink-faint) !important;
}

/* ---------------------------------------------------------------------------
   4) TOOLBAR — ghost/icon buttons. Ink-based instead of pure white/black, so
   the same three-level ink scale reads across the whole page, not just the
   canvas. Danger (clear chat) keeps its existing red: no --nbgc-* equivalent
   exists for it and it is not the "green overuse" this row is about.
   --------------------------------------------------------------------------- */
body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .ghost-btn,
body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .icon-btn,
body:is(.desktop-chat, .mobile-chat) .chat-header > .icon-btn {
  background: none !important;
  background-image: none !important;
  color: var(--nbgc-ink-soft) !important;
  /* A ghost control casts no shadow. This one line also fixes a real leftover:
     chat-mobile-flagship.css paints `.new-chat-header-btn` as a green gradient
     WITH a matching `0 2px 10px rgba(34,197,94,.35)` glow. The rule above
     already suppressed the gradient (it carries one class more), but nothing
     ever suppressed the glow — so the mobile header shipped a green halo
     around a transparent button. Measured, not spotted: the accent-budget
     harness found accent pixels around a control whose computed background is
     rgba(0,0,0,0). */
  box-shadow: none !important;
}
/* Focus must stay visible after that `box-shadow: none`. An edge, not a fill —
   the same weight the canvas uses for the closing question, and transient. */
body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .ghost-btn:focus-visible,
body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .icon-btn:focus-visible,
body:is(.desktop-chat, .mobile-chat) .chat-header > .icon-btn:focus-visible {
  box-shadow: 0 0 0 2px var(--nbgc-accent-edge) !important;
}
body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .ghost-btn:hover,
body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .icon-btn:hover,
body:is(.desktop-chat, .mobile-chat) .chat-header > .icon-btn:hover,
body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .ghost-btn:focus-visible,
body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .icon-btn:focus-visible,
body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .ghost-btn.active,
body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .icon-btn.active {
  background: var(--nbgc-surface-2) !important;
  color: var(--nbgc-ink-strong) !important;
}
body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .ghost-btn.danger,
body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .icon-btn.danger {
  background: none !important;
}

body:is(.desktop-chat, .mobile-chat) .chat-header .header-divider {
  background: var(--nbgc-hairline-strong) !important;
}

/* ---------------------------------------------------------------------------
   5) AUTOPLAY TOGGLE — the loudest thing on the whole page, measured.
   The first pass moved this off a raw #10b981 and onto var(--nbgc-accent),
   which fixed the COLOUR but not the WEIGHT: a 38x22 pill filled solid accent
   is ~700 accent pixels inside a 56px bar. The accent-budget harness
   (live/tools/_chat_accent_budget.js) put a number on it — the header spent
   3.19% of its own area on accent while the entire message canvas spent
   0.09%, i.e. 35x the density, on a control that is a SETTING, not an action.

   So the on-state keeps every cue except the fill: an accent wash on the
   track, an accent edge around it, and the thumb slid right in ink-strong
   instead of plain white. Off-state gets a quieter thumb (ink-soft) so the
   two states differ in three ways at once — position, track tint, thumb ink —
   and not just in one. Zero accent FILL remains in this bar; see the doc
   section "سلسله‌مراتب: سهمِ اکسنت" for why the header's budget is zero and
   the composer's is one.
   --------------------------------------------------------------------------- */
body:is(.desktop-chat, .mobile-chat) .chat-header .ghost-btn.autoplay-toggle .ui-toggle {
  background: var(--nbgc-surface-2) !important;
  border-color: var(--nbgc-hairline-strong) !important;
}
body:is(.desktop-chat, .mobile-chat) .chat-header .ghost-btn.autoplay-toggle .ui-toggle-thumb {
  background: var(--nbgc-ink-soft) !important;
}
body:is(.desktop-chat, .mobile-chat) .chat-header .ghost-btn.autoplay-toggle.is-on .ui-toggle {
  background: var(--nbgc-accent-wash) !important;
  border-color: var(--nbgc-accent-edge) !important;
}
body:is(.desktop-chat, .mobile-chat) .chat-header .ghost-btn.autoplay-toggle.is-on .ui-toggle-thumb {
  background: var(--nbgc-ink-strong) !important;
}
body:is(.desktop-chat, .mobile-chat) .chat-header .ghost-btn.autoplay-toggle.is-on .btn-label {
  color: var(--nbgc-accent-ink) !important;
}

/* ---------------------------------------------------------------------------
   5b) NEW CHAT (+) — the header's ONE weighted control.
   A bar where everything is ghost has no entry point, and "new conversation"
   is the only thing in this bar that is an action rather than a setting. It
   gets weight the way the canvas does — surface and ink — instead of the way
   the toggle used to, which was to grab the product's one accent. Rest sits on
   a surface with a hairline so it out-reads its ghost neighbours even before
   hover; hover steps up to --nbgc-surface-raised, which exists precisely
   because surface-2 is already the ghost hover weight.
   The icon's `stroke: #fff` from chat-mobile-flagship.css is redirected to
   currentColor: it was the only header icon painted pure white while every
   sibling was ink-soft, which read as "this one is lit up" for no reason.
   --------------------------------------------------------------------------- */
body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .new-chat-header-btn {
  background: var(--nbgc-surface-2) !important;
  background-image: none !important;
  border: 1px solid var(--nbgc-hairline-strong) !important;
  color: var(--nbgc-ink-strong) !important;
  box-shadow: none !important;
}
body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .new-chat-header-btn .ico {
  color: var(--nbgc-ink-strong) !important;
  stroke: currentColor !important;
}
body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .new-chat-header-btn:hover,
body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .new-chat-header-btn:focus-visible {
  background: var(--nbgc-surface-raised) !important;
  color: var(--nbgc-ink-strong) !important;
}

/* ---------------------------------------------------------------------------
   5c) TOUCH TARGETS — the open debt row D wrote down and deferred.
   style-mobile.css pins these to 40px so all six controls fit the 56px bar;
   growing them re-opens a layout that took several passes to settle, which is
   why row D refused to. But the target that matters is the HIT area, not the
   painted box: an ::after that bleeds 3px past the button reaches 46px without
   moving a single pixel of layout. Nothing in this header uses ::after
   (measured — every header button's computed ::after content is `none`), and
   `border-radius: inherit` keeps the hit shape honest rather than square.
   --------------------------------------------------------------------------- */
body.mobile-chat .chat-header .header-actions .ghost-btn,
body.mobile-chat .chat-header .header-actions .icon-btn,
body.mobile-chat .chat-header > .icon-btn {
  position: relative;
  overflow: visible !important;
}
body.mobile-chat .chat-header .header-actions .ghost-btn::after,
body.mobile-chat .chat-header .header-actions .icon-btn::after,
body.mobile-chat .chat-header > .icon-btn::after {
  content: '';
  position: absolute;
  inset: -3px;
  border-radius: inherit;
  /* Hit area only — it must never paint, and it must never eat the click that
     belongs to the button it belongs to (pointer events on a child still
     bubble to the button, so this widens the target instead of blocking it). */
  background: none;
}

/* ---------------------------------------------------------------------------
   6) ADMIN ROBOT SWITCHER — only rendered for admins with >1 selectable
   robot, but still `.chat-header`'s child, so still this file's scope.
   --------------------------------------------------------------------------- */
body.desktop-chat .chat-header .robot-selector select {
  background: var(--nbgc-surface) !important;
  border: 1px solid var(--nbgc-hairline) !important;
  color: var(--nbgc-ink) !important;
}

/* ---------------------------------------------------------------------------
   7) LIGHT-THEME TIE-BREAKS — see the file header. Same selector shape as the
   chat-flagship-polish.css rules being out-specificity'd, so this is a real
   tie that this file's later load order settles, not a specificity fight
   this file could lose again the next time either file is edited.
   --------------------------------------------------------------------------- */
html[data-theme="light"] body:is(.desktop-chat, .mobile-chat) .chat-header,
html[data-theme="light"] body.mobile-chat .chat-header {
  background: none !important;
  border-bottom-color: var(--nbgc-hairline) !important;
}
html[data-theme="light"] body:is(.desktop-chat, .mobile-chat) .chat-header .name {
  color: var(--nbgc-ink-strong) !important;
}
html[data-theme="light"] body.mobile-chat .chat-header .robot-status-line.active {
  color: var(--nbgc-accent-ink) !important;
}
html[data-theme="light"] body.mobile-chat .chat-header .robot-status-line.inactive {
  color: var(--nbgc-ink-faint) !important;
}
html[data-theme="light"] body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .icon-btn,
html[data-theme="light"] body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .ghost-btn,
html[data-theme="light"] body:is(.desktop-chat, .mobile-chat) .chat-header .icon-btn {
  color: var(--nbgc-ink-soft) !important;
}
/* The one weighted control has to out-rank the blanket rule right above it,
   which paints every header button ink-soft — otherwise "+" would be exactly
   as quiet as the settings gear in day theme and the hierarchy would exist
   only at night. */
html[data-theme="light"] body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .new-chat-header-btn,
html[data-theme="light"] body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .new-chat-header-btn .ico {
  color: var(--nbgc-ink-strong) !important;
}
html[data-theme="light"] body:is(.desktop-chat, .mobile-chat) .chat-header .header-actions .new-chat-header-btn {
  background: var(--nbgc-surface-2) !important;
  border-color: var(--nbgc-hairline-strong) !important;
}

/* ---------------------------------------------------------------------------
   8) THE ANCHOR BAR STAYS REACHABLE — the one rule in this file that is not
   about colour.

   Measured defect (test_controls_always_reachable.js): opening the gear's own
   menu showed #sheetOverlay, a full-viewport scrim at z-index 1250 painting
   rgba(2,6,23,.5) with a 2px backdrop blur, over a header at z-index 40. Every
   header control — gear, new chat, autoplay, theme, account, menu — stayed
   legible through the scrim and stopped answering touch. The same happened at
   z-index 1190 with #sidebarOverlay when the chats drawer opened. A control the
   user can still see has to still work; a smeared, dead copy of the header is
   worse than no header, because it invites the tap it cannot serve.

   So while a panel is open the header rises ABOVE that panel's scrim, and only
   its scrim: high enough to take clicks, low enough that the panel itself still
   draws over it (#moreSheet is 1300, .sidebar is 1200 — a header above those
   would cut the corner off the very panel it opened). script.js turns each
   class on with the panel and off again with it, and makes every header button
   dismiss the open panel before doing its own job.

   The blur is cancelled here too: the scrim's backdrop-filter samples whatever
   is painted below it, so lifting the header out of its stacking order without
   this left the header sharp but still standing in a blurred hole.
   --------------------------------------------------------------------------- */
body.mobile-chat.nabgol-sheet-open .chat-header {
  z-index: 1260 !important;   /* > #sheetOverlay 1250, < .bottom-sheet 1300 */
}
body.mobile-chat.nabgol-chats-open .chat-header {
  z-index: 1195 !important;   /* > #sidebarOverlay 1190, < .sidebar 1200 */
}
body.mobile-chat:is(.nabgol-sheet-open, .nabgol-chats-open) .chat-header {
  position: relative;         /* z-index needs a positioned box to mean anything */
  background: var(--nbgc-header-lifted) !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

/* ---------------------------------------------------------------------------
   A HEADER BUTTON THAT DRAWS NOTHING IS NOT A BUTTON

   The header's ghost buttons are icon-only at rest and grow their label on
   hover (style-desktop.css:705 collapses `.btn-label` to `max-width: 0`). That
   contract has one silent failure mode: a button appended without an icon has
   its only visible content collapsed, so it renders as bare padding. Measured
   on the real page at 1024px (2026-09-03): «ذخیرهٔ پیشرفت» and «برگرداندن
   پیشرفت» were two 16x34 boxes with nothing in them, 2px apart, between
   «تاریخچه» and «پاک کردن چت» — under the 24px minimum target and impossible to
   read, name, or aim at.

   Both were given a mark (chat-workspace.js), so this rule is the net rather
   than the fix: any future header button that arrives without an icon keeps its
   words instead of disappearing. `:not(:has(...))` costs nothing while the bar
   is fully iconised, and the failure it guards is invisibility, which is the
   one failure nobody reports as a bug.
   --------------------------------------------------------------------------- */
body.desktop-chat .chat-header .header-actions .ghost-btn:not(:has(svg)):not(:has(img)) {
  padding: 6px 12px !important;
  gap: 6px !important;
}
body.desktop-chat .chat-header .header-actions .ghost-btn:not(:has(svg)):not(:has(img)) .btn-label {
  max-width: 12rem !important;
  opacity: 1 !important;
  margin-inline-start: 0 !important;
}

/* ===========================================================================
   9) THE HEADER SITS ON THE CHAT COLUMN  (step 5 · work 10)
   docs/CHAT_REDESIGN_MASTER_PLAN.md §23, and §24.4 for the day-theme half.

   The file header above says this stylesheet does not re-litigate the header's
   geometry. That was true for a skin pass and it is no longer the assignment:
   the master plan hands the header's LAYOUT to this step by name, and this is
   the file that owns `.chat-header`. Everything below is desktop-only; the
   phone header is a different bar with a different budget and is not touched.

   THE DEFECT, MEASURED on the live site before this block existed
   (live/tools/_desktop_header_probe.js, dabir-riazi-8, both themes):

     window 1920 · chat column x 450..1270
       identity ink  x 1561..1904   — 291px PAST the column's right edge
       toolbar ink   x   16.. 309   — 141px past its left edge
       ink between them 1252px, and the whole 820px column is inside it

   So at 1920 the bar spent 100% of the width above the conversation on
   nothing, and put both islands out where the window ends. That is the
   owner's «در دو قسمتِ چپ و راست پخش شده و بالای صفحهٔ چت کاملاً خالی است»,
   as a number rather than a impression. At 1008 the identity block ran to
   x 992 while the column stopped at 792 — the name was sitting over the rail.

   THE FIX has two halves and they are deliberately separate:

     · the BAR keeps its full-bleed box, so the hairline still crosses the
       whole window and the rail still starts under a continuous line;
     · the CONTENT moves into `.chat-header-stage`, which is exactly
       --nbgc-stage wide and centred on the chat track.

   No arithmetic is repeated. The bar's padding removes the two tracks that
   are not the chat column — the rail on the physical right, the dossier on
   the physical left — using the SAME expressions chat-desktop-shell.css uses
   for the grid itself. `100%` inside the wrapper is therefore the chat track
   already, which is the one condition --nbgc-stage needs to resolve to the
   same number #messages resolves it to. §16-2 records what happens when a
   second formula is written instead: the rail gets subtracted twice.
   =========================================================================== */
@media (min-width: 768px) {

  /* A shorter bar is a taller conversation, and §23.5 asks for less rather
     than the same. 56px is what the content actually needs: a 36px avatar (or
     a 36px "+" button, the tallest control in the bar) with 10px of air above
     and below. The token is overridden rather than edited at its source
     because :root's 64px is also the fallback the grid rows carry — one place
     to look, and a robot shell that never loads this file still lays out. */
  body.desktop-chat {
    --header-height: 56px;
  }

  /* THE BAR: full-bleed box, no inline padding of its own. The padding here is
     not breathing room — it is the two grid tracks that are not the chat
     column, removed so that the wrapper's containing block IS the chat column.
     Physical left/right on purpose: the grid is LTR (chat-desktop-shell.css
     sets `direction: ltr` on body and restores rtl inside this bar), so the
     rail is always the physically-right track whatever the text direction. */
  body.desktop-chat .chat-header {
    display: flex !important;
    align-items: stretch !important;
    gap: 0 !important;
    padding-block: 0 !important;
    padding-left: 0 !important;
    padding-right: var(--nbgc-rail, 0px) !important;
    height: var(--header-height) !important;
    min-height: var(--header-height) !important;
  }
  /* Dossier open → the chat track starts after it. Same two expressions as
     chat-desktop-shell.css's `grid-template-columns`, so the bar cannot drift
     out of step with the grid when either is edited. */
  body.desktop-chat.nabgol-workspace-open .chat-header {
    padding-left: var(--ops-map-width) !important;
  }
  body.desktop-chat.nabgol-workspace-open.nabgol-workbench-on .chat-header {
    padding-left: min(var(--ops-map-width), 100% - var(--nbgc-rail) - 560px) !important;
  }

  /* THE CONTENT: one row, the column's own width, the column's own two edges.
     `flex: 0 0 auto` and an explicit width rather than `max-width`, because
     --nbgc-stage is a `min()` containing a percentage and intrinsic sizing
     drops percentages when it resolves max-content — the exact trap §16-4.2
     records for the prose measure. */
  body.desktop-chat .chat-header .chat-header-stage {
    flex: 0 0 auto !important;
    width: var(--nbgc-stage) !important;
    max-width: 100% !important;
    margin-inline: auto !important;
    display: flex !important;
    flex-direction: row !important;
    align-items: center !important;
    justify-content: space-between !important;
    gap: 12px !important;
    min-width: 0 !important;
    box-sizing: border-box !important;
  }

  /* IDENTITY — «نشان + نامِ ربات (یک خط) · توضیح یک خطِ کوچک‌تر» (§23.2).
     It may shrink (the toolbar may not: a clipped icon is a lost control,
     a clipped sentence is still a readable name), and it gets a real share of
     the column instead of the old min(34vw, 320px) that was tuned against the
     WINDOW back when the header spanned it. */
  body.desktop-chat .chat-header .chat-header-stage > .info {
    flex: 0 1 auto !important;
    min-width: 0 !important;
    max-width: min(56%, 460px) !important;
    gap: 10px !important;
    overflow: hidden !important;
  }
  /* `.info .info-text` and not `.chat-header .info-text`: style-desktop.css's
     v29 block claims all three of these with `!important` at (0,5,0), and the
     first draft of this section wrote them at (0,4,0) and lost — silently, and
     only on the LIVE page, because the numbers still measured "correct" for
     everything the probe was asking about. Caught by reading the name's
     computed font-size back off the origin after deploying, not before.
     Same shape, later file, so load order decides it. */
  body.desktop-chat .chat-header .info .info-text {
    flex: 0 1 auto !important;
    min-width: 0 !important;
    /* 240px was tuned when the header spanned the window and the identity was
       fighting the toolbar for room. On the column there is 206px of slack at
       every width from 1280 up, so the description gets to finish its sentence
       before the ellipsis takes over. */
    max-width: 340px !important;
    overflow: hidden !important;
  }
  /* Two steps of ink and two steps of size, so the pair reads as name-then-
     caption at a glance instead of as two labels. Both stay one line and both
     end in `…` rather than being cut mid-word (§23.2 asks for the ellipsis by
     name); `min-width: 0` above is what actually lets the ellipsis happen —
     without it a nowrap child refuses to shrink and overflows the bar, which
     is the CONTAINED failure test_controls_always_reachable.js watches for. */
  body.desktop-chat .chat-header .info .info-text .name {
    font-size: 15px !important;
    line-height: 1.25 !important;
    font-weight: 650 !important;
    max-width: 100% !important;
    white-space: nowrap !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
  }
  body.desktop-chat .chat-header .info .info-text .robot-desc {
    font-size: 11.5px !important;
    line-height: 1.3 !important;
    max-width: 100% !important;
    white-space: nowrap !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
  }

  /* TOOLBAR — the other end of the same row, and it never shrinks. */
  body.desktop-chat .chat-header .chat-header-stage > .header-actions {
    flex: 0 0 auto !important;
    min-width: 0 !important;
    justify-content: flex-end !important;
    padding-inline-start: 0 !important;
    overflow: visible !important;
  }

  /* THE PRESENCE PILL — §23.4.
     «فعال» is true of every robot a reader can reach: a chat page that opened
     is a robot that is on. Painted permanently next to the name it is a
     coloured badge that says nothing, and it sat BETWEEN the mark and the
     name, breaking the one-line pair §23.2 asks for. So the true-but-useless
     half is dropped and the informative half is kept, which is the reading of
     ROUTER.md's honesty rule that removes noise without removing a fact: the
     reader is still told, and only told, when the robot is NOT available.
     Desktop only — the phone header renders presence as a different element
     (`.robot-status-line`) and is out of this step's scope. */
  body.desktop-chat .chat-header .robot-status.active {
    display: none !important;
  }
  /* Inactive is a real warning, so it keeps its pill — but after the text, not
     wedged between the mark and the name. */
  body.desktop-chat .chat-header .chat-header-stage > .info > .robot-status.inactive {
    order: 3 !important;
    flex-shrink: 0 !important;
    margin-left: 0 !important;
    margin-inline-start: 2px !important;
  }
}


/* ---------------------------------------------------------------------------
   5d) ROW 2 OF THE PHONE HEADER — navigation at the start, "me + display +
   settings" at the end (owner's review, 1405-07-03).

   - «تازه» (new conversation) left the cluster where it sat between two
     display switches and a gear, as a bare "+" directly under Chrome's own
     new-tab "+". It now sits next to «مکالمات» (both are navigation), as a
     pencil + word, tinted — not a solid accent slab: "green is spent once"
     (the send button) still holds. margin-inline-end:auto opens the gap that
     turns row 2 into two groups instead of six evenly spaced glyphs.
   - Theme + read-aloud share one quiet capsule: two everyday switches, read as
     a pair. Each stays a full 44px target.
   - Read-aloud is a speaker, not a sliding switch: waves = on, cross = muted,
     waves pulse while the robot is speaking (#ttsPlayBtn.is-playing is the
     player's own state). One tap while speaking stops the voice at once —
     toggleAutoPlay() already does that; only the picture changed.
   --------------------------------------------------------------------------- */
body.mobile-chat .chat-header .nabgol-new-chat-pill.new-chat-header-btn.icon-btn {
  flex: 0 0 auto !important;
  width: auto !important;
  min-width: var(--m-header-btn) !important;
  height: var(--m-header-btn) !important;
  padding: 0 14px !important;
  gap: 6px !important;
  margin-inline-end: auto !important;
  border-radius: 999px !important;
  background: var(--nbgc-accent-wash) !important;
  background-image: none !important;
  border: 1px solid var(--nbgc-accent-edge) !important;
  box-shadow: none !important;
  color: var(--nbgc-accent-ink) !important;
  font: inherit !important;
  font-size: 14px !important;
  font-weight: 700 !important;
}
body.mobile-chat .chat-header .nabgol-new-chat-pill.new-chat-header-btn.icon-btn .ico {
  width: 19px !important;
  height: 19px !important;
  color: var(--nbgc-accent-ink) !important;
  stroke: currentColor !important;
}
body.mobile-chat .chat-header .nabgol-new-chat-pill.new-chat-header-btn.icon-btn:active {
  background: var(--nbgc-accent-edge) !important;
}
/* Too narrow for the word: a tutor robot with its progress chip on a phone
   under 400px, or any phone under 340px. The pencil alone is still nothing
   like Chrome's "+", and the row never wraps to a third line. */
@media (max-width: 399px) {
  body.mobile-chat:has(#nabgolProgressChip:not([hidden])) .chat-header .nabgol-new-chat-pill.new-chat-header-btn.icon-btn {
    padding: 0 !important;
    width: var(--m-header-btn) !important;
  }
  body.mobile-chat:has(#nabgolProgressChip:not([hidden])) .nabgol-new-chat-word { display: none !important; }
}
@media (max-width: 339px) {
  body.mobile-chat .chat-header .nabgol-new-chat-pill.new-chat-header-btn.icon-btn {
    padding: 0 !important;
    width: var(--m-header-btn) !important;
  }
  body.mobile-chat .nabgol-new-chat-word { display: none !important; }
}

body.mobile-chat .chat-header .mobile-primary-controls {
  gap: 0 !important;
  border-radius: 14px !important;
  background: var(--nbgc-surface-2) !important;
}
body.mobile-chat .chat-header .mobile-primary-controls #themeToggleBtn,
body.mobile-chat .chat-header .mobile-primary-controls #desktopAutoPlayBtn {
  background: transparent !important;
  border-color: transparent !important;
}
body:is(.desktop-chat, .mobile-chat) .chat-header .ghost-btn.autoplay-toggle .nabgol-spk {
  width: 21px !important;
  height: 21px !important;
  stroke: currentColor !important;
}
body:is(.desktop-chat, .mobile-chat) .chat-header .ghost-btn.autoplay-toggle .nabgol-spk-waves { display: none; }
body:is(.desktop-chat, .mobile-chat) .chat-header .ghost-btn.autoplay-toggle.is-on .nabgol-spk-waves { display: inline; }
body:is(.desktop-chat, .mobile-chat) .chat-header .ghost-btn.autoplay-toggle.is-on .nabgol-spk-mute { display: none; }
body:is(.desktop-chat, .mobile-chat) .chat-header #desktopAutoPlayBtn.is-on,
body:is(.desktop-chat, .mobile-chat) .chat-header #desktopAutoPlayBtn.is-on .nabgol-spk {
  color: var(--nbgc-accent-ink) !important;
}
body:has(#ttsPlayBtn.is-playing) .chat-header .ghost-btn.autoplay-toggle.is-on .nabgol-spk-waves {
  animation: nabgol-spk-pulse 1.1s ease-in-out infinite;
}
@keyframes nabgol-spk-pulse { 50% { opacity: .3; } }
@media (prefers-reduced-motion: reduce) {
  body .chat-header .ghost-btn.autoplay-toggle .nabgol-spk-waves { animation: none !important; }
}

/* «گفتگوی قبلی ذخیره شد · برگرد» — under the header, five seconds. */
.nabgol-new-chat-undo {
  position: fixed;
  top: calc(var(--nabgol-header-h, 94px) + env(safe-area-inset-top, 0px) + 10px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 1000;
  display: flex;
  align-items: center;
  gap: 14px;
  padding-block: 6px;
  padding-inline: 16px 6px;
  border-radius: 999px;
  background: var(--nbgc-ink-strong);
  color: var(--nbgc-canvas-face);
  font-size: 13px;
  white-space: nowrap;
  direction: rtl;
}
.nabgol-new-chat-undo[hidden] { display: none; }
.nabgol-new-chat-undo button {
  min-height: 36px;
  padding: 0 14px;
  border: 0;
  border-radius: 999px;
  background: var(--nbgc-accent);
  color: var(--nbgc-ink-on-accent);
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}

/* 5e) THE SAME SHAPES ON DESKTOP — one product, one vocabulary: the pencil
   pill «گفتگوی تازه» (next to «تاریخچه مکالمات», both navigation), the
   theme + speaker capsule, the same «برگرد». Desktop has the width, so the
   pill carries the full word. */
body.desktop-chat .chat-header .header-actions .nabgol-new-chat-pill.new-chat-header-btn.ghost-btn {
  display: inline-flex !important;
  align-items: center !important;
  width: auto !important;
  min-width: 0 !important;
  height: 34px !important;
  padding: 0 12px !important;
  gap: 6px !important;
  border-radius: 999px !important;
  background: var(--nbgc-accent-wash) !important;
  background-image: none !important;
  border: 1px solid var(--nbgc-accent-edge) !important;
  box-shadow: none !important;
  color: var(--nbgc-accent-ink) !important;
  font-size: 13px !important;
  font-weight: 700 !important;
  white-space: nowrap !important;
}
body.desktop-chat .chat-header .header-actions .nabgol-new-chat-pill.new-chat-header-btn.ghost-btn .ico {
  width: 17px !important;
  height: 17px !important;
  color: var(--nbgc-accent-ink) !important;
  stroke: currentColor !important;
}
body.desktop-chat .chat-header .header-actions .nabgol-new-chat-pill.new-chat-header-btn.ghost-btn:hover,
body.desktop-chat .chat-header .header-actions .nabgol-new-chat-pill.new-chat-header-btn.ghost-btn:focus-visible {
  background: var(--nbgc-accent-edge) !important;
  color: var(--nbgc-accent-ink) !important;
}
body.desktop-chat .chat-header .nabgol-display-pair {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  border-radius: 10px;
  background: var(--nbgc-surface-2);
}
body.desktop-chat .chat-header .header-actions .nabgol-display-pair .ghost-btn {
  background: transparent !important;
  border-color: transparent !important;
}
body.desktop-chat .chat-header .ghost-btn.autoplay-toggle .nabgol-spk {
  width: 18px !important;
  height: 18px !important;
}

/* ---------------------------------------------------------------------------
   10) «کارِ امروز» ON THE ROBOT'S PHOTO — phone, tutors only (U10, S3 of
   docs/CHAT_MOBILE_TUTOR_POLISH.md).
   tutor-quest.js updateHeaderChip() — the one producer of the progress
   sentence — writes --nbg-today (0..1) and .has-today-ring on the avatar; this
   section only draws it. Nothing here changes a box: the ring is painted
   INSIDE the avatar's own 34px square (it is overflow:hidden, so nothing may
   draw outside it), the photo shrinks by transform, and the pulse is a
   box-shadow. The header measures the same to the pixel with and without it
   (live/tools/test_header_today_ring.js).
   --------------------------------------------------------------------------- */
@property --nbg-today {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}
body.mobile-chat.nabgol-workbench-on #chatHeader .info .avatar.has-today-ring {
  position: relative;
  transition: --nbg-today 0.7s cubic-bezier(0.2, 0.8, 0.2, 1);
}
body.mobile-chat.nabgol-workbench-on #chatHeader .info .avatar.has-today-ring > img {
  transform: scale(0.78);
  border-radius: 6px;
}
body.mobile-chat.nabgol-workbench-on #chatHeader .info .avatar.has-today-ring::after {
  content: "";
  position: absolute;
  inset: 0;
  padding: 2.5px;
  border-radius: inherit;
  pointer-events: none;
  /* Done part in the brand accent, the rest a quiet track — at 0 the track
     alone says «today's loop is here, nothing counted yet». */
  background: conic-gradient(
    var(--nbgc-accent) calc(var(--nbg-today) * 360deg),
    var(--nbgc-hairline-strong) 0
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
}
body.mobile-chat.nabgol-workbench-on #chatHeader .info .avatar.has-today-ring.is-today-done::after {
  background: var(--nbgc-accent);
}
body.mobile-chat.nabgol-workbench-on #chatHeader .info .avatar.has-today-ring.is-today-pulse {
  animation: nbg-today-pulse 0.9s ease-out;
}
@keyframes nbg-today-pulse {
  0%   { box-shadow: 0 0 0 0 var(--nbgc-accent-edge); }
  100% { box-shadow: 0 0 0 7px transparent; }
}
@media (prefers-reduced-motion: reduce) {
  body.mobile-chat.nabgol-workbench-on #chatHeader .info .avatar.has-today-ring {
    transition: none;
  }
  body.mobile-chat.nabgol-workbench-on #chatHeader .info .avatar.has-today-ring.is-today-pulse {
    animation: none;
  }
}
