/* ============================================================================
   Stinson CRM -- toolbar layer 2 (sticky + modern finish)
   ----------------------------------------------------------------------------
   This file is deliberately the LAST stylesheet in base_site.html's extrastyle,
   after themes.css. That is not a violation of the "keep themes.css last" rule
   -- that rule exists so preset *token blocks* beat the baseline token blocks in
   admin_theme.css/borealis.css, and token definitions are unaffected by what
   loads after them. This file only consumes tokens; it never defines a preset.

   It has to load after themes.css because themes.css restates
   `body.sas-chrome #header { background: ... !important }` at (1,1,1). Equal
   specificity + later file = these win, and the `!important` flags here match
   the originals'.

   Three things happen here:

     1. The bar sticks to the top of the viewport on scroll.
     2. It is a FLOATING CARD -- inset from all three edges, rounded, elevated --
        rather than a full-bleed strip welded to the top of the document.
     3. The bar reads as a modern app chrome rather than a row of boxed buttons:
        glass, borderless ghost controls, one tinted pill for the current page.

   Everything is expressed in the existing --sas-chrome-* tokens, so all seven
   presets and any user-chosen accent carry through untouched.
   ========================================================================= */

/* ----------------------------------------------------------------------------
   0. Geometry -- and the one invariant that must not be broken

   The bar floats, so its height is now THREE numbers, not one:

     --sas-chrome-bar-h   the card itself. 52, because the nav items are 34px
                          in section 4 and 7px of air above and below a pill is
                          not enough for a row you look at all day.
     --sas-chrome-gap     how far the card is inset from the viewport top and
                          from both page edges, and the air left under it.
     --sas-chrome-h       gap + bar + gap. This is the token every OTHER page
                          consumes, and it must equal the total vertical space
                          the header occupies in layout -- nothing else.

   That last line is the whole contract. chat.css builds its layout off
   --sas-chrome-h (`height: calc(100vh - var(--sas-chrome-h))`, and the rail and
   thread sidebar pin at `top: var(--sas-chrome-h)`), and pipeline.css sticks its
   axis at the same offset, so those pages follow on their own -- but only while
   the token is the header's real footprint. The margins are part of that
   footprint now. Change the gap or the bar height and change --sas-chrome-h to
   match, or chat's composer goes below the fold.

   (flights.js measures `#flights-app`'s real top into --fl-app-top and only
   falls back to this token, so it self-corrects either way.)
   ------------------------------------------------------------------------- */
:root {
  --sas-chrome-gap: 10px;
  --sas-chrome-bar-h: 52px;
  --sas-chrome-h: 72px; /* = gap + bar-h + gap */
  --sas-chrome-r: 14px;
}

/* ----------------------------------------------------------------------------
   1. Sticky, and floating -- plus the one line that makes it work past the
      first screenful
   ------------------------------------------------------------------------- */

/* Django's base.css sets `#container { height: 100% }` against
   `html, body { height: 100% }`, so #container's box is exactly one viewport
   tall no matter how long the page is; content simply overflows it visibly.
   A sticky element is constrained to its containing block, so with that rule
   in place the toolbar would unstick the moment you scrolled past 100vh --
   it would ride the first screen down and then vanish, which is a far more
   confusing bug than no stickiness at all.

   min-height keeps every layout behaviour that depended on it (the flex column
   still fills a short page, `#container > .main { flex: 1 0 auto }` still pushes
   the footer down) while letting the box grow with the document. */
body.sas-chrome #container {
  height: auto;
  min-height: 100%;
}

/* The bar is a card that floats over the page rather than a strip welded to the
   top of it: inset by --sas-chrome-gap on both sides and above, rounded, and
   carrying its own hairline on all four edges instead of a single underline.

   `top` and the margin are the same value on purpose. At scroll-top the card
   already sits `gap` below the viewport edge, and it sticks at exactly that
   offset, so there is no jump the moment the page starts moving.

   #container is a column flex container (Django's base.css), so these margins
   cannot collapse into anything -- the header's footprint is gap + bar + gap,
   which is what --sas-chrome-h is defined as above. */
body.sas-chrome #header {
  position: sticky;
  top: var(--sas-chrome-gap);
  /* Above every page-level layer (the highest in the CRM's page stylesheets is
     2000) and below every overlay: palette 9000, chat modal 9100, calendar
     popups 9999, admin modal 12000. The toolbar must cover the page and must
     never cover a dialog. */
  z-index: 3000;
  /* The admin leaves boxes on content-box, so without this the 1px border is
     added ON TOP of the 52 and the card's real footprint is 74, not the 72 the
     token promises -- which is exactly the 20px that pushed chat's composer
     below the fold. The invariant in section 0 only holds on border-box. */
  box-sizing: border-box;
  min-height: var(--sas-chrome-bar-h);
  margin: var(--sas-chrome-gap);
  padding: 0 12px;
  gap: 12px;
  border: 1px solid var(--sas-chrome-line) !important;
  border-radius: var(--sas-chrome-r);
  /* The bar is a fixed-height strip; nothing inside it may push it taller. */
  flex: 0 0 auto;
  transition:
    box-shadow var(--sas-dur-in, 220ms) var(--sas-ease-std, cubic-bezier(0.32, 0.72, 0, 1)),
    border-color var(--sas-dur-in, 220ms) var(--sas-ease-std, cubic-bezier(0.32, 0.72, 0, 1));
}

/* Do NOT add `overflow: hidden` to clip the corners. The nav overflow menu is a
   `position: fixed` child of #site-name (chrome-refine.css), and #header's
   backdrop-filter already makes it that menu's containing block -- an overflow
   box here would clip the whole menu to the height of the bar. Nothing inside
   the bar paints outside its box anyway; the radius has nothing to clip. */

/* ----------------------------------------------------------------------------
   2. Glass

   Page content now travels underneath the bar, and a flat opaque strip with
   content disappearing under a hard edge is the thing that dates a sticky
   header. The bar takes the same material the calendar's frozen identity
   column and the Board's hero use: translucency WITH a backdrop blur. Without
   the blur, translucency reads as a rendering fault.

   88% is solved, not chosen. The worst case is the wallpaper pages, where an
   arbitrary photograph sits behind the bar at full strength with the scrim off:

     dark bar  #131316 under a white photo -> composites to rgb(47,47,50);
               --sas-chrome-fg-2 (alpha .74) over that inks to rgb(191,191,191)
               = 7.22:1
     light bar #ffffff under a black photo -> composites to rgb(224,224,224);
               --sas-chrome-fg-2 (alpha .80) over that inks to rgb(73,73,73)
               = 6.72:1

   Both clear AA 4.5:1 with room. Re-run that arithmetic against --sas-chrome-fg-2
   (the nav label ink, the quietest thing on the bar) before thinning the glass.
   ------------------------------------------------------------------------- */
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  html:not([data-sas-theme="contrast"]) body.sas-chrome #header {
    background: linear-gradient(
      180deg,
      color-mix(in srgb, var(--sas-chrome-grad-a) 88%, transparent),
      color-mix(in srgb, var(--sas-chrome-grad-b) 88%, transparent)
    ) !important;
    -webkit-backdrop-filter: blur(20px) saturate(170%);
    backdrop-filter: blur(20px) saturate(170%);
  }
}

/* ----------------------------------------------------------------------------
   3. Elevation

   This changed with the float. A flush strip needs no shadow until something
   scrolls under it -- a card that is visibly detached from all three edges is
   lying if it casts nothing, so there is a resting elevation now and
   .is-scrolled deepens it rather than introducing it. sas_chrome.js toggles the
   class; with JS off the card keeps its resting shadow and is still correct.

   The shadow is neutral black at low alpha rather than a token: it is cast on
   whatever the page happens to be (a surface, a wallpaper photograph), so it has
   to darken rather than tint.
   ------------------------------------------------------------------------- */
body.sas-chrome #header {
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.10),
    0 8px 22px -14px rgba(0, 0, 0, 0.55);
}
body.sas-chrome #header.is-scrolled {
  border-color: var(--sas-chrome-line-strong) !important;
  box-shadow:
    0 2px 4px rgba(0, 0, 0, 0.13),
    0 16px 34px -16px rgba(0, 0, 0, 0.62);
}

/* Staging: the marker is the CHIP, and nothing on the card's edge.

   borealis.css's marker was a 3px red band along the bottom edge, which the
   card cannot carry: three shapes were tried and all three read wrong. The band
   thins into nothing at both corners, so it looks like a rendering artifact
   rather than a warning. A red border plus halo outlines the whole bar in red,
   which reads as an alert box bolted to the top of every page. An inset red
   hairline sits 1px inside a 1px border and is indistinguishable from the
   outline it was meant to replace.

   So the edge stays neutral and the STAGING chip beside the logo is the marker
   -- a solid red pill in white uppercase, immediately right of the wordmark,
   which is where the eye already is. The border-color has to be restated
   because borealis.css's rule is (1,2,1); without these two rules the card gets
   a red bottom border back on the staging host only.

   These rules exist to hold that neutrality. If a louder marker is ever wanted,
   make the CHIP louder -- do not put the red back on the edge.

   The box-shadow has to be restated for the same reason and it is the easier
   one to forget: borealis.css puts the 3px red band in `box-shadow`, and the
   resting elevation above is only (1,1,1), so dropping the declaration here
   does not neutralise the band -- it hands the whole property back to borealis
   and the red returns along the bottom edge, faintly enough to look like a
   rendering bug. */
body.sas-chrome.sas-env-staging #header {
  border-color: var(--sas-chrome-line) !important;
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.10),
    0 8px 22px -14px rgba(0, 0, 0, 0.55);
}
body.sas-chrome.sas-env-staging #header.is-scrolled {
  border-color: var(--sas-chrome-line-strong) !important;
  box-shadow:
    0 2px 4px rgba(0, 0, 0, 0.13),
    0 16px 34px -16px rgba(0, 0, 0, 0.62);
}

/* ----------------------------------------------------------------------------
   4. Nav items -- one pill, no rings

   Was: every item a bordered box on hover, the current page a red-tinted box
   with a red border. Twelve outlined rectangles in a row is the look this pass
   is removing. Rest and hover carry no border at all now; hover is a tint mixed
   from the bar's own ink, so it darkens on a light bar and lightens on a dark
   one with no per-theme rule. The current page is the only filled thing in the
   row, which is what makes it findable at a glance.
   ------------------------------------------------------------------------- */
body.sas-chrome #header .sas-nav { gap: 1px; }

body.sas-chrome #header .sas-nav-item {
  height: 34px;
  padding: 0 11px;
  border: 0;
  border-radius: 9px;
  background: transparent;
  font-weight: 600;
  letter-spacing: 0.004em;
  transition:
    background var(--sas-dur-hover, 150ms) var(--sas-ease-std, cubic-bezier(0.32, 0.72, 0, 1)),
    color var(--sas-dur-hover, 150ms) var(--sas-ease-std, cubic-bezier(0.32, 0.72, 0, 1));
}

body.sas-chrome #header .sas-nav-item:hover {
  border: 0;
  background: rgba(128, 128, 128, 0.14); /* fallback where color-mix is missing */
  background: color-mix(in srgb, var(--sas-chrome-fg) 9%, transparent);
  color: var(--sas-chrome-fg) !important;
}
body.sas-chrome #header .sas-nav-item:active {
  background: color-mix(in srgb, var(--sas-chrome-fg) 15%, transparent);
}

body.sas-chrome #header .sas-nav-item[aria-current="page"] {
  border: 0;
  background: var(--sas-red-soft); /* fallback */
  background: color-mix(in srgb, rgb(var(--stinson-accent-rgb)) 15%, transparent);
  font-weight: 650;
}
body.sas-chrome #header .sas-nav-item[aria-current="page"]:hover {
  background: color-mix(in srgb, rgb(var(--stinson-accent-rgb)) 22%, transparent);
}

body.sas-chrome #header .sas-nav-i {
  width: 16px;
  height: 16px;
  stroke-width: 1.6;
  opacity: 0.72;
  transition: opacity var(--sas-dur-hover, 150ms) var(--sas-ease-std, cubic-bezier(0.32, 0.72, 0, 1));
}
body.sas-chrome #header .sas-nav-item:hover .sas-nav-i { opacity: 0.95; }

/* ----------------------------------------------------------------------------
   5. Nav overflow affordance

   Twelve items do not fit a narrow viewport. The strip has always scrolled
   horizontally with its scrollbar hidden, which meant there was no way to know
   the row continued. sas_chrome.js measures the strip and sets these classes;
   the mask fades whichever end has more behind it.

   `mask-image` creates a containing block for fixed-position descendants. Safe
   here only because the nav holds nothing but static anchors -- re-check that
   before putting a popover inside the strip.
   ------------------------------------------------------------------------- */
body.sas-chrome #header .sas-nav {
  scroll-behavior: smooth;
  scroll-padding-inline: 24px;
}
body.sas-chrome #header .sas-nav.is-overflow-end {
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 34px), transparent);
  mask-image: linear-gradient(90deg, #000 calc(100% - 34px), transparent);
}
body.sas-chrome #header .sas-nav.is-overflow-start {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 34px);
  mask-image: linear-gradient(90deg, transparent, #000 34px);
}
body.sas-chrome #header .sas-nav.is-overflow-start.is-overflow-end {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 34px, #000 calc(100% - 34px), transparent);
  mask-image: linear-gradient(90deg, transparent, #000 34px, #000 calc(100% - 34px), transparent);
}
@media (prefers-reduced-motion: reduce) {
  body.sas-chrome #header .sas-nav { scroll-behavior: auto; }
}

/* ----------------------------------------------------------------------------
   6. Brand, staging chip, dividers
   ------------------------------------------------------------------------- */
body.sas-chrome #header .sas-brand-link {
  padding: 4px 6px;
  border-radius: 9px;
  transition: background var(--sas-dur-hover, 150ms) var(--sas-ease-std, cubic-bezier(0.32, 0.72, 0, 1));
}
body.sas-chrome #header .sas-brand-link:hover {
  background: color-mix(in srgb, var(--sas-chrome-fg) 8%, transparent);
}

body.sas-chrome #header .sas-chrome-badge {
  padding: 4px 9px;
  border-radius: 999px;
  font-size: 9px;
  letter-spacing: 0.12em;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.22);
}

/* The hard 22px rule between logo and nav was as loud as the items it
   separated. Shorter, quieter, and it fades at both ends. */
body.sas-chrome #header .sas-chrome-sep {
  height: 20px;
  background: linear-gradient(
    180deg,
    transparent,
    var(--sas-chrome-line-strong) 28%,
    var(--sas-chrome-line-strong) 72%,
    transparent
  );
}

/* ----------------------------------------------------------------------------
   7. Right-hand cluster -- ghost buttons

   Was five bordered boxes in a row, each carrying the same weight as the bar
   itself. Icon buttons in a toolbar are chrome, not content: at rest they are
   just the glyph, and the surface only appears under the pointer. The hit area
   is unchanged (32px), only the paint is.
   ------------------------------------------------------------------------- */
body.sas-chrome #user-tools { gap: 2px; }

body.sas-chrome #user-tools .sas-icon-btn,
body.sas-chrome #user-tools .theme-toggle {
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: 9px;
  background: transparent;
  color: var(--sas-chrome-fg-2) !important;
  transition:
    background var(--sas-dur-hover, 150ms) var(--sas-ease-std, cubic-bezier(0.32, 0.72, 0, 1)),
    color var(--sas-dur-hover, 150ms) var(--sas-ease-std, cubic-bezier(0.32, 0.72, 0, 1));
}
body.sas-chrome #user-tools .sas-icon-btn:hover,
body.sas-chrome #user-tools .theme-toggle:hover {
  border: 0;
  background: color-mix(in srgb, var(--sas-chrome-fg) 10%, transparent);
  color: var(--sas-chrome-fg) !important;
}
body.sas-chrome #user-tools .sas-icon-btn:active,
body.sas-chrome #user-tools .theme-toggle:active {
  background: color-mix(in srgb, var(--sas-chrome-fg) 17%, transparent);
}
/* Log out is the one destructive control up here, so it is the one that gets
   the accent -- and only on hover. A permanently red control in a row you use
   every day is a klaxon you learn to ignore. */
body.sas-chrome #user-tools .sas-icon-btn.sas-danger:hover {
  border: 0;
  background: color-mix(in srgb, rgb(var(--stinson-accent-rgb)) 18%, transparent);
}
body.sas-chrome #user-tools .sas-icon-btn svg,
body.sas-chrome #user-tools .theme-toggle svg {
  width: 16px;
  height: 16px;
  stroke-width: 1.6;
}

/* The name is a label, not a button -- it loses its outline and keeps only
   enough tint to read as a group with the icons beside it. */
body.sas-chrome #user-tools .sas-user-name {
  height: 26px;
  margin-inline-end: 4px;
  padding: 0 10px;
  border: 0;
  background: color-mix(in srgb, var(--sas-chrome-fg) 8%, transparent);
  color: var(--sas-chrome-fg-2);
  letter-spacing: 0.08em;
}

/* ----------------------------------------------------------------------------
   8. Keep the bar one row below 1024px

   Django's responsive.css does `@media (max-width: 1024px) { #header {
   flex-direction: column } }`. Two consequences, and the second is the one
   CLAUDE.md has been carrying as "known, not fixed":

     - the bar becomes two stacked rows, which for a bar that is now pinned to
       the top means it eats that height out of every screen, permanently;
     - a column flex container gives #branding no reason to shrink, so the nav
       strip renders at its full content width (~1200px) and pushes the whole
       *document* sideways. The strip's own `overflow-x: auto` never engages,
       which is why the horizontal scrollbar was on the document rather than on
       the nav.

   Holding the row restores the shrink, and the strip absorbs the overflow into
   its own scroll with the section 5 fade marking it. Measured at 760px:
   document scrollWidth 1074 -> 760, no horizontal page scroll.

   responsive.css loads after this file, but `#header` is (1,0,0) against
   (1,1,1) here, so specificity decides it and load order does not matter.
   ------------------------------------------------------------------------- */
@media (max-width: 1024px) {
  body.sas-chrome #header {
    flex-direction: row;
    justify-content: space-between;
    padding: 0 10px;
  }
  body.sas-chrome #header #site-name { margin: 0; }
  body.sas-chrome #user-tools { margin: 0; }
}

/* On a phone the gutters are the scarcest thing on the page, so the card tucks
   in closer. --sas-chrome-h is restated with it: the invariant in section 0 is
   that the token equals gap + bar + gap at whatever width it is read. */
@media (max-width: 640px) {
  :root {
    --sas-chrome-gap: 6px;
    --sas-chrome-h: 64px;
    --sas-chrome-r: 12px;
  }
}

/* ----------------------------------------------------------------------------
   9. High Contrast opts out of the whole soft treatment

   That preset's loud borders are deliberate. Glass, borderless controls and
   9%-alpha tints are all exactly what it exists not to do, so it keeps outlined
   controls -- restated here because section 4 and 7 above zero the borders.
   ------------------------------------------------------------------------- */
html[data-sas-theme="contrast"] body.sas-chrome #header .sas-nav-item:hover,
html[data-sas-theme="contrast"] body.sas-chrome #header .sas-nav-item[aria-current="page"],
html[data-sas-theme="contrast"] body.sas-chrome #user-tools .sas-icon-btn,
html[data-sas-theme="contrast"] body.sas-chrome #user-tools .theme-toggle {
  border: 1px solid var(--sas-chrome-line-strong);
}
html[data-sas-theme="contrast"] body.sas-chrome #header .sas-nav-item[aria-current="page"] {
  background: var(--sas-red-soft);
  border-color: var(--sas-red-line);
}
html[data-sas-theme="contrast"] body.sas-chrome #user-tools .sas-user-name {
  border: 1px solid var(--sas-chrome-line-strong);
}
/* The card shape is structure, not softness, so High Contrast keeps it -- but
   it gets a hard edge and a flat drop instead of the layered one, and no halo:
   this preset's whole point is that an edge is a line, not a gradient. */
html[data-sas-theme="contrast"] body.sas-chrome #header {
  border-width: 2px !important;
  border-color: var(--sas-chrome-line-strong) !important;
  box-shadow: 0 4px 0 -1px rgba(0, 0, 0, 0.55);
}
html[data-sas-theme="contrast"] body.sas-chrome.sas-env-staging #header,
html[data-sas-theme="contrast"] body.sas-chrome.sas-env-staging #header.is-scrolled {
  border-color: var(--sas-chrome-line-strong) !important;
  box-shadow: 0 4px 0 -1px rgba(0, 0, 0, 0.55);
}

@media (prefers-reduced-motion: reduce) {
  body.sas-chrome #header,
  body.sas-chrome #header .sas-nav-item,
  body.sas-chrome #header .sas-nav-i,
  body.sas-chrome #header .sas-brand-link,
  body.sas-chrome #user-tools .sas-icon-btn,
  body.sas-chrome #user-tools .theme-toggle {
    transition: none;
  }
}

/* SAS-TOGGLE-BEGIN */
/* ============================================================================
   The theme toggle joins the cluster
   ----------------------------------------------------------------------------
   The BEGIN/END markers above and below are single-line and stand alone on
   purpose: patch_toggle.py strips every copy of the block between them before
   appending a fresh one, and a marker buried inside a larger comment makes that
   match ambiguous -- it can start at an earlier `/*` and swallow real CSS.

   Appended to chrome.css by work/icon/patch_toggle.py. Consumes existing
   tokens only; defines no preset and no literal colour.

   Section 7 above turned #user-tools into ghost buttons -- transparent at rest,
   a surface only under the pointer. It rewrote `.sas-icon-btn` and
   `.theme-toggle` themselves, but the toggle's chip is drawn on
   `.theme-toggle::before` (admin_theme.css), which appears in none of those
   selector lists. So a `border-radius: 50%` capsule survived in a row of 9px
   ghosts, and it is the reason the toggle reads as a different KIND of control
   from the four icons beside it.

   That capsule's dark branch is a hardcoded navy gradient with a
   `rgba(56, 189, 248, .45)` sky-blue border -- the same slate/sky palette leak
   that was cleaned out of admin_subpages.css, admin_modal.css and
   admin_index.css, and never caught here. Every dark preset sets
   data-theme="dark", so Graphite, Forest and Midnight all drew a sky-blue ring.

   THE GLYPH NEEDS ITS OWN PARAGRAPH, because the obvious fix does not work.
   Django's dark_mode.css sets, on the SVG itself:

       .theme-toggle svg.theme-icon-when-dark { fill:  var(--header-link-color);
                                                color: var(--header-bg); }

   `--header-bg` is Django's own `var(--primary)`, and themes.css sets --primary
   to the BRAND RED. Django's markup then carries `<path fill="currentColor">`
   as a presentation attribute, which beats the `fill` the path would otherwise
   inherit from the SVG -- so the glyph resolves to --header-bg and paints solid
   #c1272d. admin_theme.css then adds `stroke: currentColor` at 1.6 over that
   fill, thickening it further.

   This is why section 7's `color: ... !important` ON THE BUTTON is inert: the
   colour that matters is re-declared on the SVG, one level down, and the
   attribute reads it from there. Setting `fill` explicitly is the only fix, and
   it has to beat (0,2,1) for Django's SVG rule and (0,1,2) for admin_theme's
   `.theme-toggle svg path`. `body.sas-chrome #user-tools .theme-toggle svg` is
   (1,2,1) and clears both.

   Keep this AFTER section 7, and keep chrome.css after themes.css, for the
   reason at the top of this file.
   ========================================================================= */

/* The capsule goes. `content: none` rather than `display: none` -- it is
   cheaper, and it leaves nothing for admin_theme.css's :hover/:focus/:active
   ::before rules to act on. Press and hover feedback still exist; section 7
   puts them on the button, where the siblings carry theirs. */
body.sas-chrome #user-tools .theme-toggle::before {
  content: none;
}

/* Stroke, like every sibling in the cluster. `fill: none` is what un-paints
   Django's crescent; `color: inherit` is what pulls the ink back from the SVG's
   own `color: var(--header-bg)` to the button's --sas-chrome-fg-2, so the glyph
   now brightens on hover with the rest of the row. See the specificity note
   above for why this selector is written the long way. */
body.sas-chrome #user-tools .theme-toggle svg,
body.sas-chrome #user-tools .theme-toggle svg path,
body.sas-chrome #user-tools .theme-toggle svg circle {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  color: inherit;
}

/* The toggle was also the one control up here with no visible focus ring.
   borealis.css lists .sas-brand-link, .sas-nav-item and .sas-icon-btn; the
   toggle instead had admin_theme.css's `outline: none` answered by a shadow on
   the ::before this block just removed -- so once the capsule goes, keyboard
   focus would land on nothing at all. Restate it on the button, matching the
   9px ghost radius rather than the capsule's 50%. */
body.sas-chrome #user-tools .theme-toggle:focus-visible {
  outline: 2px solid var(--sas-chrome-fg);
  outline-offset: 2px;
  border-radius: 9px;
}
/* SAS-TOGGLE-END */

/* SAS-FULLBLEED-BEGIN */
/* ============================================================================
   The toolbar is a full-bleed strip, not a floating card
   ----------------------------------------------------------------------------
   Reverses section 1's float. The bar spans the full page width, welded to the
   top of the viewport: no side gutters, no top gap, no radius, and a single
   hairline along the bottom edge instead of an outline on all four.

   This block is APPENDED, so it wins on load order at equal specificity.
   Sections 0-3 above still read as the floating card and are left byte-exact on
   purpose -- `--revert` puts the card back by deleting this block and nothing
   else. Read the sections above as history; this block is the live geometry.

   THE @media (max-width: 640px) BLOCK ABOVE IS NOW DEAD. It restates gap 6 /
   h 64 / r 12 for phones, and every one of those three is overridden here by a
   later non-media rule, so it applies at no width. Left in place for the same
   revert reason. Do not "fix" the phone geometry there -- it will not take.

   Three tokens do almost all of the work, because sections 0-3 express the
   card's geometry through them rather than as literals:

     --sas-chrome-gap  0  ->  `top` and `margin` both collapse (section 1)
     --sas-chrome-r    0  ->  `border-radius` collapses (section 1)
     --sas-chrome-h       ->  the footprint every OTHER page consumes

   THE INVARIANT FROM SECTION 0 STILL HOLDS AND IS NOW AUTOMATIC. --sas-chrome-h
   must equal the header's real footprint in layout; with the margins gone that
   is exactly the bar's own height, so it is written as a var() rather than a
   number and cannot drift if --sas-chrome-bar-h is ever retuned. It moves
   72 -> 52, and chat.css (`height: calc(100vh - var(--sas-chrome-h))`, the rail
   and thread sidebar `top:`), pipeline.css (its sticky axis and every
   scroll-margin-top), field_tracking.html (the feed rail's max-height) and
   flights.css's fallback all follow on their own. flights.js and chat.js
   measure their own real top instead, so they self-correct either way.
   ========================================================================= */
:root {
  --sas-chrome-gap: 0px;
  --sas-chrome-r: 0px;
  --sas-chrome-h: var(--sas-chrome-bar-h);
}

/* The outline becomes an underline.

   `border: 0` zeroes all four; the bottom is then re-declared. Both carry
   !important to match borealis.css's `border-bottom: ... !important` (1,1,1),
   which this beats on load order.

   Section 9's High Contrast rule (`border-width: 2px !important`, (1,2,2)) is
   deliberately NOT overridden and needs no change: a border whose style is
   `none` computes to zero width whatever the width property says, so that rule
   now thickens the bottom hairline to 2px and leaves the other three absent --
   which is what that preset wants from a flush strip. */
body.sas-chrome #header {
  border: 0 !important;
  border-bottom: 1px solid var(--sas-chrome-line) !important;
}

/* No resting elevation.

   This is section 3's own reasoning, applied in the direction it was written
   for: "A flush strip needs no shadow until something scrolls under it -- a
   card that is visibly detached from all three edges is lying if it casts
   nothing." The card is gone, so the resting shadow goes with it and
   .is-scrolled INTRODUCES the elevation rather than deepening it.

   Both .is-scrolled rules above are (1,2,1) and are left untouched, so they
   still win whenever sas_chrome.js has set the class. With the script blocked
   the bar is a flat strip with a hairline, which is correct and complete. */
body.sas-chrome #header {
  box-shadow: none;
}

/* Staging has to be restated, and it is the easy one to miss.

   `body.sas-chrome.sas-env-staging #header` is (1,2,1) and beats the (1,1,1)
   rule above, so without this the staging host keeps a resting shadow that
   prod does not have. It also has to keep doing the job the section 3 comment
   describes: borealis.css puts its 3px red band in `box-shadow`, so this
   declaration is what holds the edge neutral. `none` neutralises it exactly as
   the explicit shadow did.

   NOTE FOR LATER: borealis.css's red band was ruled out because it "thins into
   nothing at both corners" on a rounded card. That objection no longer applies
   to a square, full-bleed bar -- but the decision that the STAGING CHIP is the
   marker and the edge stays neutral was taken on its own merits and is not
   reopened here. If a louder marker is ever wanted, make the chip louder. */
body.sas-chrome.sas-env-staging #header {
  box-shadow: none;
}

/* The bar got wider; its contents do not move.

   The old optical inset from the viewport edge was margin + border + padding:
   23px by default, 21px under 1024, 17px under 640. With the margin and the
   side border gone that has to come back as padding, or the logo and the user
   cluster jump ~11px outward the moment this block lands and stop lining up
   with the page content below them.

   The 1024 rule is restated because the rule it overrides lives in a media
   query above and this block's default would otherwise apply at every width. */
body.sas-chrome #header {
  padding: 0 22px;
}
@media (max-width: 1024px) {
  body.sas-chrome #header { padding: 0 20px; }
}
@media (max-width: 640px) {
  body.sas-chrome #header { padding: 0 16px; }
}
/* SAS-FULLBLEED-END */

/* SAS-CHATBADGE-BEGIN */
/* ===========================================================================
   Team Chat -- the toolbar unread badge, and the arrival toast.

   Appended to chrome.css rather than borealis.css because everything here is
   toolbar finish, which is what this file is for, and because chrome.css
   already loads last: the badge restates nothing that borealis.css declares,
   but the More-menu dot rules sit at the same specificity as the ones they
   sit beside, so being later is what keeps them predictable.

   EVERY colour here is a --sas-chrome-* token or is solved against one. The
   bar flips light/dark per preset, so a literal would strand Light and Sand --
   the trap documented against the theme toggle's capsule. The two literals
   that ARE here are the mention pill's ink, and both branches are written out
   (see the High Contrast note below), because that is a case where following
   the token is precisely the wrong thing to do.
   =========================================================================== */

/* Screen-reader-only text. Django's admin ships no such class we can rely on
   here, so it is declared once and used by the badge. `clip-path` rather than
   the old clip rect, and it keeps a 1px box: a zero-size element is skipped by
   some screen readers entirely. */
body.sas-chrome .sas-vh {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------------------------
   The pill on the Chat item
   --------------------------------------------------------------------------- */

/* The nav row's gap is 8px, which is right between an icon and a label and too
   much between a label and the number that belongs to it -- at 8px the badge
   reads as a separate control sitting next to Chat rather than as part of it.
   -3px lands it at 5. */
body.sas-chrome #header .sas-nav-item .sas-nav-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  margin-left: -3px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--sas-chrome-line-strong);
  color: var(--sas-chrome-fg);
  font-size: 10px;
  font-weight: 800;
  line-height: 1;
  letter-spacing: 0;   /* the item tracks -0.006em; digits do not want it */
  font-variant-numeric: tabular-nums;
  /* 1 -> 2 must not move the label. Tabular figures plus a min-width that
     already fits two glyphs means the pill only grows at "9+". */
}

/* borealis.css forces [hidden] for exactly this reason -- display:inline-flex
   beats the UA's [hidden] rule -- but that rule is scoped to its own list of
   selectors, so this one states it for itself rather than relying on being
   covered. Without it the badge renders as an empty pill when the count is
   zero, which reads as a broken control. */
body.sas-chrome #header .sas-nav-item .sas-nav-badge[hidden] { display: none !important; }

/* HIGH CONTRAST INVERTS THE NEUTRAL PILL, and this is not a style preference.
   That preset sets --sas-chrome-line-strong to rgba(255,255,255,0.60) -- it
   uses the token as a LOUD BORDER, not as a fill -- and --sas-chrome-fg to
   #ffffff. The rule above therefore composites to white-on-near-white at
   **2.46:1**. Inverting to the bar's own fg/bg pair cannot fail contrast by
   construction (19.8:1 here), and a solid white pill with black digits is that
   preset's vocabulary anyway. Measured by work/chrome/audit_chat_badge.py. */
html[data-sas-theme="contrast"] body.sas-chrome #header .sas-nav-item .sas-nav-badge {
  background: var(--sas-chrome-fg);
  color: var(--sas-chrome-surface);
}

/* Mentioned. This is the only place red is spent on the toolbar, and it is the
   same grey-then-red step chat.css's own rail badge makes -- `--sas-red` with
   white ink is `.sc-badge.is-mention` exactly, so the two surfaces say the
   same thing in the same colour. (`--sas-red-bright` was the first choice and
   is the wrong one twice over: it is a hover/focus value, and it measures
   4.61:1 against this pill's 5.84:1.)

   THE INK IS A LITERAL, DELIBERATELY, AND HIGH CONTRAST IS WRITTEN OUT.
   `--sas-red` is #c1272d in six presets and **#ffd400 -- yellow -- in High
   Contrast**, which redefines the whole red ramp. So the obvious `color: #fff`
   is white-on-yellow at **1.30:1** in that one preset: not "a little low",
   unreadable -- and invisible to any audit that reads only the base token
   values. Black-on-yellow is what that preset does everywhere else. */
body.sas-chrome #header .sas-nav-item .sas-nav-badge.is-mention {
  background: var(--sas-red);
  color: #ffffff;
}
html[data-sas-theme="contrast"] body.sas-chrome #header .sas-nav-item .sas-nav-badge.is-mention {
  background: var(--sas-red);
  color: #0a0a0a;
}

/* On the current page the item's own ink goes full strength; the badge should
   not fight it. It cannot happen on /chat/ (the badge is not rendered there at
   all) but it can in the More menu, where the panel restyles the items. */
body.sas-chrome #site-name .sas-nav-more-panel .sas-nav-item .sas-nav-badge {
  margin-left: auto;   /* panel items are full-width rows: push it to the end */
}

/* ---------------------------------------------------------------------------
   The More menu is carrying it
   --------------------------------------------------------------------------- */

/* admin_nav_overflow.js MOVES the item, so the badge goes with it and is then
   inside a closed panel -- invisible, in exactly the case it exists for. This
   is a corner-anchored notification dot, deliberately a different shape and
   place from the menu's existing inline `has-current` "you are here" dot, so a
   button showing both is still readable as two separate facts.

   position: relative on the button is safe: it holds the label, the caret and
   the existing dot, all in normal flow, and none of them is positioned. */
body.sas-chrome #header .sas-nav-more-btn { position: relative; }

body.sas-chrome #header .sas-nav-more.has-unread .sas-nav-more-btn::after {
  content: "";
  position: absolute;
  top: 1px;
  right: 1px;
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: var(--sas-red-bright);
  /* A ring in the bar's own colour is what stops the dot dissolving into the
     caret behind it. It is the surface token, not a literal, so it follows the
     bar light or dark. */
  box-shadow: 0 0 0 2px var(--sas-chrome-surface);
}

/* ---------------------------------------------------------------------------
   The toast
   --------------------------------------------------------------------------- */

/* The live region. Always in the document, empty almost all of the time -- a
   live region inserted already-populated is missed by several screen readers,
   and one that is display:none never announces at all. It must therefore never
   be given a background, a border or a size of its own. */
.sas-chat-toasts {
  position: fixed;
  right: 16px;
  bottom: 16px;
  /* Above the page and the sticky toolbar, below the command palette (9000)
     and its overlay (9100). A chat notification must not cover a control the
     user has deliberately opened. */
  z-index: 8500;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: min(360px, calc(100vw - 32px));
  pointer-events: none;   /* the empty region must not eat clicks on the page */
}

.sas-chat-toast {
  position: relative;
  display: block;
  pointer-events: auto;
  padding: 10px 30px 11px 12px;
  border-radius: var(--sas-r-md, 10px);
  border: 1px solid var(--sas-line, rgba(255, 255, 255, 0.09));
  background: var(--sas-bg-1);
  color: var(--sas-fg) !important;   /* beats Django's a:link, a:visited */
  text-decoration: none;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28), 0 2px 6px rgba(0, 0, 0, 0.18);
  /* The entrance. Starting state is here and `.is-in` is added on the next
     frame; both are transitioned, so a browser that never gets the class (a
     hidden document, where rAF does not fire) is handled in the JS instead of
     being left at opacity 0. */
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 180ms ease, transform 180ms cubic-bezier(0.2, 0.86, 0.28, 1);
}
.sas-chat-toast.is-in { opacity: 1; transform: none; }
.sas-chat-toast:hover { background: var(--sas-bg-2); }
.sas-chat-toast:focus-visible {
  outline: 2px solid var(--sas-accent, var(--sas-red-bright));
  outline-offset: 2px;
}

/* Mentioned: a rail down the leading edge, not a red card. The toast already
   has the whole of the viewport corner to itself -- a saturated card there is
   the klaxon this admin keeps deciding not to build. */
.sas-chat-toast.is-mention { border-left: 3px solid var(--sas-red-bright); }

.sas-chat-toast-head {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-bottom: 3px;
  font-size: var(--sas-t-sm, 12.5px);
  line-height: 1.3;
}
.sas-chat-toast-head b { font-weight: 700; color: var(--sas-fg); }
.sas-chat-toast-head span {
  color: var(--sas-fg-2);
  font-size: var(--sas-t-xs, 11.5px);
  /* The conversation name is the part that can be long -- a DM to somebody
     with a long name, or a channel. Truncate it rather than the sender. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sas-chat-toast-body {
  color: var(--sas-fg-2);
  font-size: var(--sas-t-sm, 12.5px);
  line-height: 1.4;
  /* Two lines, then ellipsis. The server already caps the preview at 90
     characters; this is what stops a 90-character run of one word from
     making a tall card. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.sas-chat-toast-x {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--sas-fg-3);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}
.sas-chat-toast-x:hover { background: var(--sas-bg-3); color: var(--sas-fg); }
.sas-chat-toast-x:focus-visible {
  outline: 2px solid var(--sas-accent, var(--sas-red-bright));
  outline-offset: 1px;
}

/* On a narrow screen the corner is not a corner -- span the width instead of
   leaving a 16px sliver of page down one side. */
@media (max-width: 480px) {
  .sas-chat-toasts { left: 16px; right: 16px; max-width: none; }
}

/* Reduced motion drops the displacement and shortens the fade rather than
   removing the transition: clear() times its teardown off the declared
   duration, and `transition: none` there would be a toast that never leaves.
   The same call the page-transition layer makes. */
@media (prefers-reduced-motion: reduce) {
  .sas-chat-toast {
    transform: none;
    transition: opacity 80ms linear;
  }
  .sas-chat-toast.is-in { transform: none; }
}
/* SAS-CHATBADGE-END */

/* SAS-BAR3-BEGIN */
/* ============================================================================
   Toolbar, third pass -- composition, the current-page state, and the cluster
   ----------------------------------------------------------------------------
   Appended to chrome.css by work/chrome/patch_barrefine.py, so it wins on load
   order at equal specificity. Consumes existing tokens only; defines no preset
   and no literal colour except two neutral hairlines.

   Read the earlier sections as history where they disagree: section 1 describes
   a floating card that SAS-FULLBLEED already reversed, and section 4's
   current-page pill is what this block rebuilds.

   Four things happen here:

     1. The nav is centred in the space between the brand and the tools, so a
        wide bar reads as a composition rather than as two clusters flung at
        opposite ends of a mostly-empty strip.
     2. The current-page pill is rebuilt on the BRAND, not on
        --stinson-accent-rgb, which is the legacy sky blue on six of the seven
        presets (see below). It also gains an underline, because a tint alone is
        a weak "you are here" on a bar that may be sitting on a photograph.
     3. The right-hand cluster becomes one family of shapes, and the one
        destructive control in it is fenced off.
     4. The nav icons stop compounding two alphas down to near-invisibility.

   CONTRAST IS AUDITED OFFLINE, NOT BY EYE AND NOT IN THE PREVIEW PANE --
   work/chrome/audit_bar.py, which solves every figure below against the worst
   backdrop each preset can face (88% glass over a white photograph on a dark
   bar, over a black one on a light bar, scrim off). Re-run it before changing
   any value here. Current worst cases across all seven presets:

     accent ink on the current pill   4.55  (sand)
     nav label on the bar             5.86  (sand)
     nav icon at .82                  4.01  (sand)
     the underline rail               4.55  (sand)   -- see the note on the rail

   ========================================================================= */

/* ----------------------------------------------------------------------------
   A. The nav is centred in its own free space

   The bar is brand-left / tools-right with everything between them empty. At
   1600px that is 433px of nothing; on the 2560px displays this CRM is actually
   used on it is over a thousand. Centring the items inside the nav strip
   balances it: measured at 1600px the gaps come out 236px before and 223px
   after, and the block sits 40px left of the true bar centre -- which is
   correct, because it is centred in the space that is actually free rather than
   in the viewport.

   THE STRIP ITSELF STILL FILLS THE BAR, AND THAT IS LOAD-BEARING. .sas-nav
   keeps `flex: 1 1 auto` from chrome-refine.css section A -- admin_nav_overflow.js
   sizes the More menu off nav.clientWidth, and the moment the strip is
   content-sized instead, moving one item out shrinks the strip, which shrinks
   the budget, which pushes another item out. That collapse is documented in
   CLAUDE.md and it is why this is `justify-content` and not a width, a margin
   or a position. Neither clientWidth nor scrollWidth changes here.

   `safe` is not optional. A centred flex container that overflows puts the
   start of its content in negative space where no amount of scrolling can
   reach it -- so the first nav items would be unreachable at narrow widths.
   The `safe` keyword falls back to `start` exactly when that would happen.

   Gated at 1200px because that is where all seven items plus both clusters
   stop fitting (items ~669px + brand ~199 + tools ~258 + gaps ~24 = ~1150).
   Below it the overflow menu is already doing the work and there is no slack
   to centre into, so the nav stays hugged to the brand where it belongs. */
@media (min-width: 1200px) {
  body.sas-chrome #header .sas-nav {
    justify-content: safe center;
  }

  /* The separator divides the brand from the nav. Once the nav is centred they
     are ~230px apart and it is a rule between two things that are no longer
     adjacent -- it reads as a stray mark hanging off the logo. It comes back
     on its own below 1200px, where the nav is hugged to the brand again and it
     is doing its job.

     `visibility` rather than `display` so the strip's layout, and therefore
     every width admin_nav_overflow.js has measured, is completely unchanged. */
  body.sas-chrome #header .sas-chrome-sep {
    visibility: hidden;
  }
}

/* ----------------------------------------------------------------------------
   B. The current page is the BRAND -- it was sky blue

   --stinson-accent-rgb is `14, 165, 233` at admin_theme.css:10's :root, and the
   ONLY preset that restates it is High Contrast (to yellow). So on light, dark,
   midnight, graphite, forest and sand -- six of the seven -- section 4's
   `color-mix(... rgb(var(--stinson-accent-rgb)) 15% ...)` paints the
   current-page pill SKY BLUE. Measured on the deployed dark preset it
   composites to #122936, a desaturated navy, while its ink is
   --sas-chrome-accent-ink = #ffdfe0, a red-derived near-white. The fill and the
   ink were from different hues, and neither was the brand.

   This is the same defect class CLAUDE.md already records twice -- the
   slate/sky palette left in admin_subpages.css/admin_modal.css/admin_index.css,
   and the date scaffolding built from the accent -- and this is the last place
   in the toolbar still doing it.

   The fix is to build the pill on the RED ramp, which is genuinely per-preset:
   --sas-red is #c1272d everywhere and #ffd400 in High Contrast, and
   --sas-chrome-accent-ink is already its matching ink (#8c1c21 on the two light
   bars, #ffdfe0 from :root on the four dark ones, #fff6bd in High Contrast). So
   fill and ink agree on hue in all seven, and High Contrast stays yellow for
   free without a single branch.

   NOTE: this deliberately takes the current-page pill OFF the user-chosen
   accent. A user who picks a colour in Settings still gets it everywhere else;
   here the marker is brand furniture, the same category as the STAGING chip
   beside it, and one hue for "you are here" across every preset is worth more
   than one more surface following the picker. The root cause -- that the
   DEFAULT accent is a leftover sky blue rather than the brand red -- is a
   separate, wider fix: it also makes the log-out hover blue (section D) and
   reaches well past this bar. */
body.sas-chrome #header .sas-nav-item[aria-current="page"] {
  position: relative;
  background: var(--sas-red-soft); /* fallback where color-mix is missing */
  background: color-mix(in srgb, var(--sas-red) 18%, transparent);
  color: var(--sas-chrome-accent-ink) !important;
}
body.sas-chrome #header .sas-nav-item[aria-current="page"]:hover {
  background: color-mix(in srgb, var(--sas-red) 26%, transparent);
}

/* The underline.

   A tint is a weak state marker on a bar that is 88% glass over an arbitrary
   photograph -- on the wallpaper pages the fill has very little to work with.
   The underline is what survives that, because it is a solid mark rather than a
   wash.

   IT IS `currentColor`, AND THAT IS THE WHOLE REASON IT PASSES. The obvious
   choice is the brand red, and the audit rejects it: #c1272d as a lone
   graphical mark measures 2.02-2.28:1 against a dark bar with a bright
   photograph behind it, and --sas-red-bright only lifts that to 2.55-2.88 --
   both under the 3:1 floor WCAG 1.4.11 sets for a non-text indicator. The brand
   red is simply too dark to be a mark on a near-black bar. Inheriting the
   item's own accent ink instead means the rail's contrast IS the label's
   figure, by construction, in every preset and against every backdrop --
   4.55:1 worst case. It also cannot drift if the ink token is ever retuned.

   Inset 10px each side so it reads as a mark under the label rather than as a
   bottom border on the pill, and it stops short of the pill's rounded corners.
   Absolutely positioned, so it adds no width -- admin_nav_overflow.js measures
   these items and a rail that changed their width would change what fits. */
body.sas-chrome #header .sas-nav-item[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: 10px;
  right: 10px;
  bottom: 3px;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
}

/* The current item's icon carries the accent ink at full strength. Section 4
   holds it at .72, which on the one item that is meant to be findable is the
   only glyph in the row wearing two alphas at once. */
body.sas-chrome #header .sas-nav-item[aria-current="page"] .sas-nav-i {
  opacity: 1;
}

/* ----------------------------------------------------------------------------
   C. Nav icons -- two alphas were compounding

   .sas-nav-i is `opacity: .72` and the item's colour is --sas-chrome-fg-2,
   which is itself alpha .74 on the dark presets and .80 on the light ones. The
   glyph therefore renders at an effective ~.53 -- measured 3.30:1 on Sand,
   under the 3:1-for-icons floor once a bright wallpaper is behind the bar.

   .82 puts the worst case at 4.01:1 and leaves the icon still visibly quieter
   than its label, which is the hierarchy the original .72 was reaching for. */
body.sas-chrome #header .sas-nav-i {
  opacity: 0.82;
}
body.sas-chrome #header .sas-nav-item:hover .sas-nav-i {
  opacity: 1;
}

/* ----------------------------------------------------------------------------
   D. Log out hovers RED, not sky blue

   Section 7 gives the one destructive control in the cluster the accent on
   hover, and says why: "Log out is the one destructive control up here, so it
   is the one that gets the accent." It then spends --stinson-accent-rgb on it,
   which is the sky blue from section B -- so the control that means "end your
   session" currently hovers a friendly blue on six of the seven presets, and
   the warning it was written to carry has never once been shown.

   --sas-red-bright rather than --sas-red: this is a hover state on a dark bar,
   which is exactly the case borealis.css defines the brighter step for. High
   Contrast redefines both to yellow, so that preset follows with no branch. */
body.sas-chrome #user-tools .sas-icon-btn.sas-danger:hover {
  background: color-mix(in srgb, var(--sas-red) 20%, transparent);
  color: var(--sas-red-bright) !important;
}
body.sas-chrome #user-tools .sas-icon-btn.sas-danger:active {
  background: color-mix(in srgb, var(--sas-red) 30%, transparent);
}

/* ----------------------------------------------------------------------------
   E. The right-hand cluster is one family of shapes, and the destructive
      control is fenced

   Six 32px ghost buttons at a 9px radius, and then the user name as a 999px
   lozenge with a border and a filled surface -- a shape that appears nowhere
   else in the row, reading as a control that does nothing when pressed. It is
   the only bordered object in a cluster whose whole design is borderless.

   It keeps its tint (it does need to group with the icons rather than float as
   bare text) but takes their geometry: same height, same radius, no border. */
body.sas-chrome #user-tools .sas-user-name {
  height: 32px;
  border: 0;
  border-radius: 9px;
  background: color-mix(in srgb, var(--sas-chrome-fg) 7%, transparent);
}

/* Search is the entry point to the command palette and the most-used control in
   this cluster by a wide margin, and at rest it is indistinguishable from Help.
   A faint resting surface is enough to say "this one is the way in" without
   making it a button -- it still lifts to the normal hover tint under the
   pointer, so the affordance is unchanged. */
body.sas-chrome #user-tools .sas-icon-btn[data-sas-palette] {
  background: color-mix(in srgb, var(--sas-chrome-fg) 6%, transparent);
}

/* Fence the log out.

   It sits flush against the theme toggle, so the destructive control is one
   32px step from a control people press casually all day. A hairline before it
   is the "destructive fenced under a rule" move the calendar's bar-actions
   panel already makes.

   It goes on the FORM, not the button: borealis.css makes #logout-form an
   inline-flex wrapper, so the pseudo-element is a flex sibling of the button
   and takes part in the row rather than painting inside it. The margins give it
   the air the cluster's 2px gap does not. */
body.sas-chrome #user-tools form#logout-form::before {
  content: "";
  align-self: center;
  width: 1px;
  height: 18px;
  margin-inline: 5px 3px;
  background: var(--sas-chrome-line-strong);
}

/* ----------------------------------------------------------------------------
   F. Motion

   Section 4 and 7 transition `background` and `color` on these controls; the
   new declarations reuse those properties, so they inherit the existing timing
   with nothing added. Only the icon opacity is new. The reduced-motion block at
   the end of the base file lists selectors rather than tokens, so it does not
   reach this one and it is restated here.
   ------------------------------------------------------------------------- */
body.sas-chrome #header .sas-nav-i {
  transition: opacity var(--sas-dur-hover, 150ms)
              var(--sas-ease-std, cubic-bezier(0.32, 0.72, 0, 1));
}
@media (prefers-reduced-motion: reduce) {
  body.sas-chrome #header .sas-nav-i {
    transition: none;
  }
}

/* ----------------------------------------------------------------------------
   G. High Contrast keeps its outlines

   Section 9 restates a real 1px border on the controls sections 4 and 7 zero
   out, and this block zeroes two more of them (.sas-user-name in section E) --
   so that preset's border has to come back here too, or the one preset whose
   entire point is that an edge is a line loses it on the user name.

   The current-page pill needs nothing: section B hands it --sas-red /
   --sas-red-soft, which High Contrast has already redefined to yellow, and
   section 9's `background: var(--sas-red-soft); border-color: var(--sas-red-line)`
   is (0,3,2)+id and still wins the fill. The underline inherits the yellow ink
   and measures 12.31:1 there.
   ------------------------------------------------------------------------- */
html[data-sas-theme="contrast"] body.sas-chrome #user-tools .sas-user-name {
  border: 1px solid var(--sas-chrome-line-strong);
  border-radius: 9px;
}
/* This preset opts out of the soft tints everywhere else, so the search button
   does not get a resting one either -- it keeps the outlined treatment section
   9 gives every icon button. */
html[data-sas-theme="contrast"] body.sas-chrome #user-tools .sas-icon-btn[data-sas-palette] {
  background: transparent;
}
/* SAS-BAR3-END */
