/* ============================================================================
   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;
  }
}
