/* ============================================================================
   Stinson CRM -- Borealis design layer
   ----------------------------------------------------------------------------
   Tokens + shared primitives + the global admin chrome (top toolbar), styled
   after the Borealis desktop app: neutral near-black surfaces, hairline
   borders, brand red for active/primary state, compact uppercase micro-labels,
   tile grids.

   Palette is sampled from the logo file itself: red #C1272D, grey #CCCCCC.

   Load order matters -- this file must come AFTER admin_theme.css so its
   chrome rules win. Primitives (.sas-panel, .sas-tiles, ...) are opt-in
   classes and have no effect until a template uses them.
   ========================================================================= */

:root {
  /* brand */
  --sas-red: #c1272d;
  --sas-red-bright: #d8383e;
  --sas-red-soft: rgba(193, 39, 45, 0.15);
  --sas-red-line: rgba(216, 56, 62, 0.55);
  --sas-grey: #cccccc;

  /* surfaces, darkest to lightest */
  --sas-bg-0: #0a0a0b;
  --sas-bg-1: #101012;
  --sas-bg-2: #16161a;
  --sas-bg-3: #1e1e23;

  /* lines */
  --sas-line: rgba(255, 255, 255, 0.08);
  --sas-line-strong: rgba(255, 255, 255, 0.15);

  /* ink */
  --sas-fg: #f1f1f2;
  --sas-fg-2: rgba(241, 241, 242, 0.74);
  --sas-fg-3: rgba(241, 241, 242, 0.54);

  /* radii */
  --sas-r-sm: 7px;
  --sas-r-md: 10px;
  --sas-r-lg: 14px;

  /* type scale -- explicit px, because html{font-size:110%} inflates rem */
  --sas-t-micro: 10.5px;
  --sas-t-xs: 11.5px;
  --sas-t-sm: 12.5px;
  --sas-t-md: 14px;
  --sas-t-lg: 17px;

  --sas-chrome-h: 48px;

  /* The toolbar is dark in both themes, so its ink and surfaces must NOT flip
     with the theme tokens above -- otherwise light theme paints dark text on
     the dark bar. */
  --sas-chrome-fg: #f1f1f2;
  --sas-chrome-fg-2: rgba(241, 241, 242, 0.74);
  --sas-chrome-fg-3: rgba(241, 241, 242, 0.54);
  --sas-chrome-surface: #16161a;
  --sas-chrome-surface-hover: #1e1e23;
  --sas-chrome-line: rgba(255, 255, 255, 0.09);
  --sas-chrome-line-strong: rgba(255, 255, 255, 0.16);
}

/* ----------------------------------------------------------------------------
   Light theme. Borealis itself is dark-only, but the CRM ships a theme toggle
   and staff do run it in light, so the same design language is expressed with
   light surfaces rather than forcing dark on everyone. Only the tokens flip --
   component rules below reference tokens exclusively.

   The chrome (#header) stays dark in both themes: it keeps the white-wordmark
   logo readable and matches Borealis's dark toolbar.
   ------------------------------------------------------------------------- */
html[data-theme="light"] {
  --sas-bg-0: #eef1f6;
  --sas-bg-1: #ffffff;
  --sas-bg-2: #f4f7fb;
  --sas-bg-3: #e7ecf4;
  --sas-line: rgba(11, 35, 64, 0.13);
  --sas-line-strong: rgba(11, 35, 64, 0.24);
  --sas-fg: #16233a;
  --sas-fg-2: rgba(22, 35, 58, 0.78);
  --sas-fg-3: rgba(22, 35, 58, 0.56);
  --sas-red-soft: rgba(193, 39, 45, 0.09);
  --sas-red-line: rgba(193, 39, 45, 0.45);
}
@media (prefers-color-scheme: light) {
  html[data-theme="auto"] {
    --sas-bg-0: #eef1f6;
    --sas-bg-1: #ffffff;
    --sas-bg-2: #f4f7fb;
    --sas-bg-3: #e7ecf4;
    --sas-line: rgba(11, 35, 64, 0.13);
    --sas-line-strong: rgba(11, 35, 64, 0.24);
    --sas-fg: #16233a;
    --sas-fg-2: rgba(22, 35, 58, 0.78);
    --sas-fg-3: rgba(22, 35, 58, 0.56);
    --sas-red-soft: rgba(193, 39, 45, 0.09);
    --sas-red-line: rgba(193, 39, 45, 0.45);
  }
}

/* Filtering hooks from admin_index.js -- [hidden] must beat display:grid/flex */
.sas-group[hidden],
.sas-mtile[hidden],
.stinson-admin-nav-item[hidden] { display: none !important; }

/* ============================================================================
   1. Global chrome -- the top toolbar
   ========================================================================= */

body.sas-chrome #header {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: var(--sas-chrome-h);
  padding: 0 14px;
  background: linear-gradient(180deg, #131316, #0d0d0f) !important;
  border-bottom: 1px solid var(--sas-chrome-line) !important;
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.6), 0 8px 24px -18px rgba(0, 0, 0, 0.9);
  color: var(--sas-chrome-fg);
}
/* Staging: a red underline instead of a full red wash, so the bar keeps its
   Borealis look while still being unmistakably not-production. */
body.sas-chrome.sas-env-staging #header {
  border-bottom: 1px solid var(--sas-red) !important;
  box-shadow: inset 0 -3px 0 -1px rgba(193, 39, 45, 0.55);
}

body.sas-chrome #branding {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0;
  padding: 0;
  min-width: 0;
  flex: 1 1 auto;
}
body.sas-chrome #branding h1,
body.sas-chrome #site-name {
  margin: 0;
  padding: 0;
  font-size: inherit;
  font-weight: inherit;
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
  flex: 1 1 auto;
}

body.sas-chrome #header .sas-brand-link {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  padding: 4px 2px;
  border-radius: var(--sas-r-sm);
}
body.sas-chrome #header .sas-brand-logo {
  display: block;
  width: auto;
  height: 24px;
}
body.sas-chrome #header .sas-brand-link:focus-visible,
body.sas-chrome #header .sas-nav-item:focus-visible,
body.sas-chrome #user-tools .sas-icon-btn:focus-visible {
  outline: 2px solid var(--sas-red-bright);
  outline-offset: 2px;
}

body.sas-chrome #header .sas-chrome-badge {
  flex: 0 0 auto;
  padding: 3px 7px;
  border-radius: 5px;
  background: var(--sas-red);
  color: #fff;
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  line-height: 1.4;
}

body.sas-chrome #header .sas-chrome-sep {
  flex: 0 0 auto;
  width: 1px;
  height: 22px;
  background: var(--sas-chrome-line-strong);
}

/* --- nav -------------------------------------------------------------- */
body.sas-chrome #header .sas-nav {
  display: flex;
  align-items: center;
  gap: 2px;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}
body.sas-chrome #header .sas-nav::-webkit-scrollbar { display: none; }

body.sas-chrome #header .sas-nav-item {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex: 0 0 auto;
  height: 32px;
  padding: 0 10px;
  border: 1px solid transparent;
  border-radius: var(--sas-r-sm);
  color: var(--sas-chrome-fg-2) !important;
  font-size: var(--sas-t-sm);
  font-weight: 600;
  letter-spacing: 0.005em;
  text-decoration: none !important;
  white-space: nowrap;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
body.sas-chrome #header .sas-nav-item:hover {
  background: var(--sas-chrome-surface-hover);
  border-color: var(--sas-chrome-line);
  color: var(--sas-chrome-fg) !important;
}
body.sas-chrome #header .sas-nav-item[aria-current="page"] {
  background: var(--sas-red-soft);
  border-color: var(--sas-red-line);
  color: #ffdfe0 !important;
}
body.sas-chrome #header .sas-nav-i {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.85;
}
body.sas-chrome #header .sas-nav-item[aria-current="page"] .sas-nav-i { opacity: 1; }

/* --- right-hand user cluster ------------------------------------------ */
body.sas-chrome #user-tools {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  padding: 0;
  flex: 0 0 auto;
  font-size: var(--sas-t-xs);
  text-transform: none;
  letter-spacing: 0;
}
body.sas-chrome #user-tools .sas-user-name {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 9px;
  border: 1px solid var(--sas-chrome-line);
  border-radius: 999px;
  background: var(--sas-chrome-surface);
  color: var(--sas-chrome-fg-2);
  font-size: var(--sas-t-micro);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  white-space: nowrap;
}
body.sas-chrome #user-tools .sas-icon-btn {
  display: inline-grid;
  border-bottom: 1px solid var(--sas-chrome-line);
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid var(--sas-chrome-line);
  border-radius: var(--sas-r-sm);
  background: var(--sas-chrome-surface);
  color: var(--sas-chrome-fg-2) !important;
  cursor: pointer;
  text-decoration: none !important;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
body.sas-chrome #user-tools .sas-icon-btn:hover {
  background: var(--sas-chrome-surface-hover);
  border-color: var(--sas-chrome-line-strong);
  color: var(--sas-chrome-fg) !important;
}
body.sas-chrome #user-tools .sas-icon-btn.sas-danger:hover {
  background: var(--sas-red-soft);
  border-color: var(--sas-red-line);
  color: #ffdfe0 !important;
}
body.sas-chrome #user-tools .sas-icon-btn svg {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
body.sas-chrome #user-tools form#logout-form {
  display: inline-flex;
  margin: 0;
  padding: 0;
}
/* The theme toggle ships as its own button; normalise it into the cluster. */
body.sas-chrome #user-tools .theme-toggle {
  width: 30px;
  height: 30px;
  border: 1px solid var(--sas-chrome-line);
  border-radius: var(--sas-r-sm);
  background: var(--sas-chrome-surface);
  color: var(--sas-chrome-fg-2);
  margin: 0;
}
body.sas-chrome #user-tools .theme-toggle:hover {
  background: var(--sas-chrome-surface-hover);
  border-color: var(--sas-chrome-line-strong);
}
body.sas-chrome #user-tools .theme-toggle svg { width: 15px; height: 15px; }

/* ============================================================================
   2. Shared primitives (opt-in -- used by the console, reusable elsewhere)
   ========================================================================= */

.sas-panel {
  background: var(--sas-bg-1);
  border: 1px solid var(--sas-line);
  border-radius: var(--sas-r-lg);
}
.sas-panel-pad { padding: 14px; }

.sas-label {
  display: block;
  margin: 0 0 8px;
  font-size: var(--sas-t-micro);
  font-weight: 700;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--sas-fg-3);
}

.sas-pills {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: var(--sas-bg-2);
  border: 1px solid var(--sas-line);
  border-radius: 999px;
}
.sas-pill {
  appearance: none;
  border: 1px solid transparent;
  border-radius: 999px;
  background: transparent;
  color: var(--sas-fg-2);
  padding: 5px 13px;
  font-family: inherit;
  font-size: var(--sas-t-xs);
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
}
.sas-pill[aria-selected="true"],
.sas-pill.is-active {
  background: var(--sas-red-soft);
  border-color: var(--sas-red-line);
  color: #ffdfe0;
}

.sas-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
  gap: 8px;
}
.sas-tile {
  display: block;
  position: relative;
  padding: 10px 11px;
  border: 1px solid var(--sas-line);
  border-radius: var(--sas-r-md);
  background: var(--sas-bg-2);
  color: var(--sas-fg) !important;
  text-decoration: none !important;
  transition: background 120ms ease, border-color 120ms ease, transform 90ms ease;
}
.sas-tile:hover {
  background: var(--sas-bg-3);
  border-color: var(--sas-red-line);
}
.sas-tile:active { transform: translateY(1px); }

.sas-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 22px;
  padding: 0 8px;
  border: 1px solid var(--sas-line);
  border-radius: 999px;
  background: var(--sas-bg-2);
  color: var(--sas-fg-3);
  font-size: var(--sas-t-micro);
  font-weight: 700;
  letter-spacing: 0.06em;
}

@media (prefers-reduced-motion: reduce) {
  #header .sas-nav-item, #user-tools .sas-icon-btn, .sas-tile { transition: none; }
  .sas-tile:active { transform: none; }
}
/* SAS-CHROME2-BEGIN ========================================================
   Toolbar refinement pass + the nav overflow menu.

   Appended rather than edited in place: borealis.css is contended, and every
   rule here restates a selector from section 1 above at the SAME specificity,
   so being later in the file is what makes it win. Keep it last, and keep the
   `body.sas-chrome #header` / `#user-tools` scoping -- admin_index.css,
   admin_subpages.css and Django's responsive.css all load AFTER this file and
   style #header/#user-tools with id selectors, which outrank bare classes
   regardless of order.

   Nothing here hardcodes a colour. The bar flips light/dark per preset, so
   every value is a --sas-chrome-* token; a literal would strand Light and Sand
   (see "The toolbar now themes" in CLAUDE.md).

   What changed, and why:

   - The nav no longer scrolls. It was `overflow-x: auto` with the scrollbar
     hidden, which at 1440px left 277px of items off-screen with no affordance
     -- a quarter of the primary navigation, and *which* quarter depended on
     the page, because the browser scrolled the current item into view.
     admin_nav_overflow.js now moves whatever does not fit into a menu.
   - Hover and the current item lose their 1px ring. Twelve pills each capable
     of drawing a border is a lot of line work in a 48px bar; fill alone
     separates them, and the border is kept transparent so nothing reflows.
   - The right-hand cluster loses its per-button boxes. Six bordered, filled
     30px squares in a row read as a control panel bolted to the end of the
     bar; as ghost buttons they read as one cluster and the bar gets quieter.
   ========================================================================= */

/* --- bar ---------------------------------------------------------------- */

body.sas-chrome #header {
  gap: 12px;
  padding: 0 12px;
}

body.sas-chrome #header .sas-chrome-sep {
  height: 18px;
  background: var(--sas-chrome-line);
}

body.sas-chrome #header .sas-chrome-badge {
  padding: 3px 7px;
  border-radius: 6px;
  font-size: 9px;
  letter-spacing: 0.11em;
}

/* --- nav ---------------------------------------------------------------- */

/* `hidden`, not `auto`. The overflow menu is what makes the row fit, and a
   scrollbar here would only hide the fact that it had failed. It is also a
   backstop for the moment before the JS has measured. */
/* `flex: 1 1 auto` is load-bearing, not tidying. Section 1 leaves this at the
   `0 1 auto` default, which sizes the nav to its CONTENT -- fine while it
   simply overflowed, fatal once items can leave it: moving one out shrinks the
   nav, which shrinks the budget, which pushes another one out. It collapsed to
   three items in a 356px nav inside 918px of free bar. Growing to fill makes
   clientWidth a property of the layout instead of the child count, which is
   the fixed point the measuring in admin_nav_overflow.js needs. */
body.sas-chrome #header .sas-nav {
  flex: 1 1 auto;
  gap: 3px;
  overflow: hidden;
}

body.sas-chrome #header .sas-nav-item {
  height: 30px;
  gap: 8px;
  padding: 0 10px;
  border: 1px solid transparent;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  /* Slightly negative tracking. The old +0.005em was widening labels that are
     already fighting for room, and open tracking at 13px/600 reads dated. */
  letter-spacing: -0.006em;
  color: var(--sas-chrome-fg-2) !important;
  transition:
    background-color 130ms ease,
    color 130ms ease,
    box-shadow 130ms ease;
}

body.sas-chrome #header .sas-nav-item:hover {
  background: var(--sas-chrome-surface-hover);
  border-color: transparent;
  color: var(--sas-chrome-fg) !important;
}

body.sas-chrome #header .sas-nav-item[aria-current="page"] {
  background: var(--sas-red-soft);
  border-color: transparent;
  /* A 1px inner highlight instead of a full ring: it lifts the pill off the
     bar without drawing a second outline around it. Inert on a light bar,
     which is the correct behaviour there. */
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
  color: var(--sas-chrome-accent-ink) !important;
}

/* 16px at 1.6 rather than 15px at 1.7 -- a lighter stroke on a slightly
   larger glyph is what makes these read as drawn rather than as clip art at
   this size. The rest state is quieter so the row of labels leads. */
body.sas-chrome #header .sas-nav-i {
  width: 16px;
  height: 16px;
  stroke-width: 1.6;
  opacity: 0.62;
  transition: opacity 130ms ease;
}
body.sas-chrome #header .sas-nav-item:hover .sas-nav-i { opacity: 0.92; }
body.sas-chrome #header .sas-nav-item[aria-current="page"] .sas-nav-i { opacity: 1; }

/* --- overflow menu ------------------------------------------------------ */

/* Hidden until the JS decides it is needed, so a browser that never runs it
   shows the plain (clipped) row it always did rather than a dead button. */
body.sas-chrome #header .sas-nav-more {
  display: none;
  flex: 0 0 auto;
}
body.sas-chrome #header .sas-nav-more.is-on { display: inline-flex; }

body.sas-chrome #header button.sas-nav-more-btn {
  cursor: pointer;
  background: transparent;
  font-family: inherit;
}
body.sas-chrome #header button.sas-nav-more-btn[aria-expanded="true"] {
  background: var(--sas-chrome-surface-hover);
  color: var(--sas-chrome-fg) !important;
}
body.sas-chrome #header .sas-nav-more-caret {
  width: 12px;
  height: 12px;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.7;
  transition: transform 160ms ease;
}
body.sas-chrome #header button.sas-nav-more-btn[aria-expanded="true"] .sas-nav-more-caret {
  transform: rotate(180deg);
}

/* The current page can be inside the menu. A count alone would not say so,
   and silently losing the only "you are here" marker is the one thing this
   menu must not do. */
body.sas-chrome #header .sas-nav-more-btn .sas-nav-more-dot {
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--sas-red-bright);
  display: none;
}
body.sas-chrome #header .sas-nav-more.has-current .sas-nav-more-dot { display: block; }

/* Mounted as a sibling of nav inside #site-name, NOT inside nav: the nav is
   `overflow: hidden`, and it is also the one zone base_site.html's
   target="_blank" hook leaves alone, so links moved in here keep opening in
   the same tab like the bar items they came from. */
body.sas-chrome #site-name .sas-nav-more-panel {
  position: fixed;
  z-index: 3000;
  min-width: 200px;
  max-height: min(70vh, 560px);
  overflow-y: auto;
  overflow-x: hidden;
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  border: 1px solid var(--sas-chrome-line-strong);
  border-radius: 12px;
  background: var(--sas-chrome-surface);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.04) inset,
    0 18px 44px -12px rgba(0, 0, 0, 0.7),
    0 4px 12px -6px rgba(0, 0, 0, 0.5);
}
body.sas-chrome #site-name .sas-nav-more-panel[hidden] { display: none; }

/* In the panel the items are a list, not a row of pills: full width, left
   aligned, no centring. */
/* No `width: 100%` here. The panel is a flex column, so the items already
   stretch -- and the admin leaves boxes on content-box, so an explicit 100%
   plus the 10px padding and 1px border overflowed by 22px and put a
   horizontal scrollbar across the bottom of the menu. box-sizing is restated
   as a belt for anything that sets a width later. */
body.sas-chrome #site-name .sas-nav-more-panel .sas-nav-item {
  box-sizing: border-box;
  height: 32px;
  justify-content: flex-start;
  border-radius: 8px;
}

@media (prefers-reduced-motion: reduce) {
  body.sas-chrome #header .sas-nav-item,
  body.sas-chrome #header .sas-nav-i,
  body.sas-chrome #header .sas-nav-more-caret { transition: none; }
}

/* --- right-hand cluster -------------------------------------------------- */

body.sas-chrome #user-tools { gap: 2px; }

body.sas-chrome #user-tools .sas-user-name {
  height: 26px;
  margin-right: 6px;
  padding: 0 10px;
  border-color: transparent;
  background: var(--sas-chrome-surface-hover);
  letter-spacing: 0.08em;
}

body.sas-chrome #user-tools .sas-icon-btn {
  width: 30px;
  height: 30px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: transparent;
  color: var(--sas-chrome-fg-3) !important;
  transition: background-color 130ms ease, color 130ms ease;
}
body.sas-chrome #user-tools .sas-icon-btn:hover {
  background: var(--sas-chrome-surface-hover);
  border-color: transparent;
  color: var(--sas-chrome-fg) !important;
}
body.sas-chrome #user-tools .sas-icon-btn svg {
  width: 16px;
  height: 16px;
  stroke-width: 1.6;
}
/* SAS-CHROME2-END ========================================================= */

/* SAS-VT-APPICON-BEGIN =====================================================
   The Borealis nav item, paired with the portal's boot-splash tile.

   `sas-vt-appicon` is declared on BOTH documents: here on the toolbar icon,
   and on .bs-tile in the Borealis build's boot splash. A view-transition-name
   present in the old and the new document is what makes the browser morph one
   element into the other rather than cross-fading the whole page, so the icon
   you click flies to the centre of the screen and becomes the app tile, which
   then blooms open into the viewer.

   Selected by href, deliberately: no template change, and it cannot drift
   from the route. The nav overflow menu MOVES items rather than cloning them
   (see admin_nav_overflow.js), so this stays exactly one element per document
   even when Borealis is inside More — which matters, because a duplicate
   view-transition-name aborts the entire transition silently.

   Timings are symmetric here, unlike root and sas-vt-navmark. Those two
   restate `isolation: auto` because asymmetric opacities sum above 1 under
   the UA's plus-lighter blending and blow out to white. Matching the old and
   new exactly keeps them complementary, so this group needs no such rule.
   ========================================================================= */
.sas-nav-item[href="/borealis/"] .sas-nav-i { view-transition-name: sas-vt-appicon; }

:root::view-transition-old(sas-vt-appicon),
:root::view-transition-new(sas-vt-appicon) {
  animation-duration: var(--sas-vt-in, 420ms);
  animation-timing-function: var(--sas-vt-ease-out, cubic-bezier(0.16, 1, 0.3, 1));
}
/* SAS-VT-APPICON-END ===================================================== */

/* SAS-VT-BEGIN =============================================================
   Page transitions (cross-document View Transitions)

   Clicking a toolbar item is a full document navigation -- the admin is a
   multi-page app and nothing here is a SPA. `@view-transition` is what lets
   the browser animate between two separately-loaded documents, so this needs
   no router, no fetch and no JavaScript at all: both documents opt in, and
   Chrome holds the old paint on screen until the new one is ready.

   Three groups, and the split IS the design:

     sas-vt-chrome   #header. Both documents draw an identical toolbar, so a
                     snapshot of one held over the other is perfectly still --
                     the navigation stops reading as "the page reloaded" and
                     starts reading as "the pane under the toolbar changed".
                     It is captured precisely so that it does NOT animate:
                     unnamed, it would be part of the root snapshot and would
                     fade and rise along with the content beneath it.
     sas-vt-navmark  the current-page pill. The same name on a different
                     element in each document is what makes a group morph, so
                     the red pill glides from the item you left to the one you
                     picked instead of blinking across the bar.
     root            everything else -- content, breadcrumbs, footer, and the
                     wallpaper. Fades, and rises slightly.

   Tuning is the tokens below and nothing else. Anything that does not opt in
   (the Borealis portal, ops) navigates exactly the way it always did; there
   is no fallback to write and no JS to load.

   Sections 4 and 5 are about ONE navigation, signing in, which is the only
   one in the admin where the old document has no toolbar and where the two
   pages share a backdrop. Read them before touching sections 1 or 3 -- the
   sign-in case is what the `:only-child` rules exist to rescue.
   ========================================================================= */

@view-transition { navigation: auto; }

:root {
  /* Entrances run roughly twice the exit, on the decelerating curve -- the
     same asymmetry the calendar's motion pass uses. Symmetric timing is what
     made the old transitions read as sluggish. These are deliberately longer
     than an in-page hover (150ms): a whole page changing is a bigger event
     and wants to be seen. */
  --sas-vt-in: 420ms;
  --sas-vt-out: 180ms;
  --sas-vt-ease-out: cubic-bezier(0.16, 1, 0.3, 1);   /* expo-out */
  --sas-vt-ease-in: cubic-bezier(0.7, 0, 0.84, 0);
  --sas-vt-rise: 12px;

  /* Section 5 only. Kept up here with the rest so the whole layer is still
     tuned from one block. */
  --sas-vt-signin-out: 340ms;
  --sas-vt-signin-scale: 0.965;
  --sas-vt-signin-drift: 8px;
  --sas-vt-signin-lag: 110ms;
}

/* Independent old/new timings need plain alpha compositing.

   The UA stylesheet isolates each image pair and blends the two snapshots
   with `plus-lighter`, which is correct only while their opacities are
   complementary -- it is what stops a cross-fade dipping dark in the middle.
   Every pair below runs the old and the new on different curves AND different
   durations, so their opacities sum above 1 partway through, and under
   plus-lighter that does not read as "slightly bright", it blows out to
   white. Normal blending cannot flash; the new page simply arrives over the
   old one. Do not remove these two rules while the timings stay asymmetric.
   ========================================================================= */
:root::view-transition-image-pair(root),
:root::view-transition-image-pair(sas-vt-navmark) { isolation: auto; }
:root::view-transition-old(root),
:root::view-transition-new(root),
:root::view-transition-old(sas-vt-navmark),
:root::view-transition-new(sas-vt-navmark) { mix-blend-mode: normal; }

/* --- 1. the toolbar holds still ---------------------------------------- */

body.sas-chrome #header { view-transition-name: sas-vt-chrome; }

:root::view-transition-group(sas-vt-chrome) { animation-duration: var(--sas-vt-in); }
:root::view-transition-old(sas-vt-chrome),
:root::view-transition-new(sas-vt-chrome) { animation: none; opacity: 1; }

/* --- 2. the current-page pill slides ------------------------------------ */

/* Scoped to .sas-nav deliberately. A view-transition-name must be unique in
   the document, and a duplicate does not degrade gracefully -- it aborts the
   whole transition, silently, with nothing in the console. The gear in
   #user-tools also carries aria-current on /settings/, so it is left out
   rather than made to share this name. */
body.sas-chrome #header .sas-nav .sas-nav-item[aria-current="page"] {
  view-transition-name: sas-vt-navmark;
}

/* The shape travels for the full duration; the two labels swap early, so a
   ghost of the word you left is not dragged the width of the bar. */
:root::view-transition-group(sas-vt-navmark) {
  animation-duration: var(--sas-vt-in);
  animation-timing-function: var(--sas-vt-ease-out);
}
:root::view-transition-old(sas-vt-navmark) {
  animation: sas-vt-fade-out 150ms linear both;
}
:root::view-transition-new(sas-vt-navmark) {
  animation: sas-vt-fade-in 150ms linear both;
}

/* --- 3. the page under it ----------------------------------------------- */

:root::view-transition-old(root) {
  animation:
    sas-vt-fade-out var(--sas-vt-out) var(--sas-vt-ease-in) both,
    sas-vt-sink var(--sas-vt-out) var(--sas-vt-ease-in) both;
}
:root::view-transition-new(root) {
  animation:
    sas-vt-fade-in var(--sas-vt-in) var(--sas-vt-ease-out) both,
    sas-vt-rise var(--sas-vt-in) var(--sas-vt-ease-out) both;
}

/* The wallpaper is part of the root snapshot (it is painted by body::before
   and ::after), so on the three pages that carry one the rise would drag the
   photograph too -- two copies of the same picture 12px apart, cross-fading,
   which reads as ghosting rather than as movement. Those pages cross-fade in
   place. Check this attribute before hunting a wallpaper bug elsewhere. */
html[data-sas-calbg="1"]::view-transition-old(root) {
  animation: sas-vt-fade-out var(--sas-vt-out) var(--sas-vt-ease-in) both;
}
html[data-sas-calbg="1"]::view-transition-new(root) {
  animation: sas-vt-fade-in var(--sas-vt-in) var(--sas-vt-ease-out) both;
}

@keyframes sas-vt-fade-out { to { opacity: 0; } }
@keyframes sas-vt-fade-in { from { opacity: 0; } }
@keyframes sas-vt-sink { to { transform: translateY(calc(var(--sas-vt-rise) * -0.4)); } }
@keyframes sas-vt-rise { from { transform: translateY(var(--sas-vt-rise)); } }

/* --- 4. arriving from a page that has no toolbar ------------------------ */

/* Section 1 pins the chrome at full strength with no animation, and that is
   exactly right while BOTH documents draw a toolbar: the snapshot of one held
   over the other is perfectly still. It is wrong for the one navigation with
   no old toolbar to hold -- signing in. `#header` is `display: none
   !important` on /login/ and on the sign-out page, so it takes no snapshot at
   all, the group is new-only, and `animation: none; opacity: 1` means the
   whole bar SNAPS in at full strength while the page under it is still
   fading. That reads as the toolbar arriving before the app it belongs to.

   `:only-child` is how the pseudo tree says "this group has no counterpart":
   the old and new images are siblings inside ::view-transition-image-pair, so
   an old-only or new-only group has exactly one child. Both directions are
   written out -- signing OUT is the mirror, and the sign-out page loads this
   same file. Higher specificity than section 1 and later in the file, so it
   wins on both counts.  */
:root::view-transition-new(sas-vt-chrome):only-child {
  animation:
    sas-vt-fade-in var(--sas-vt-in) var(--sas-vt-ease-out) both,
    sas-vt-rise var(--sas-vt-in) var(--sas-vt-ease-out) both;
}
:root::view-transition-old(sas-vt-chrome):only-child {
  animation: sas-vt-fade-out var(--sas-vt-out) var(--sas-vt-ease-in) both;
}

/* --- 5. signing in ------------------------------------------------------- */

/* Sign-in is not a toolbar click and must not animate like one.

   The sign-in page is a windowed card floating on a FULL-VIEWPORT Daylight
   sky (`.sas-login-sky` is `position: fixed; inset: 0`), and the Board's
   wallpaper is that same scene, computed for the same moment, in the same
   place. So the two documents already agree about the backdrop, and the whole
   job of this section is to not disturb it: the sky holds still and cross-
   dissolves into itself while the card recedes and the console arrives
   underneath. Give the root ANY displacement here and the sky visibly jumps
   at the moment of arrival, which is the one thing the eye is guaranteed to
   be looking at.

   This is a view-transition TYPE rather than an attribute, because the styles
   for a cross-document transition come from the NEW document and the new
   document cannot otherwise know where it was entered from. The login page
   adds `sas-signin` in its `pageswap` handler, and only when the destination
   really is the Board -- a wrong password lands back on the sign-in page, and
   that navigation wants the plain in-place cross-fade of two identical
   layouts, not a card that recedes and comes back.

   `:active-view-transition-type()` is Chrome 125+. A browser that does not
   know the pseudo-class drops these rules as invalid selectors and gets the
   generic transition above, which is why every one of them is its own rule --
   grouped with a supported selector, the unsupported one would take the whole
   rule down with it. Nothing here needs a fallback.  */

/* Declared on the OLD document's element (this file loads on the sign-in page
   too, via base_site.html's extrastyle). Naming it lifts the card out of the
   root snapshot, which is what leaves the root holding nothing but the sky.
   `.sas-login-shell` is already a stacking context and the sky is its
   SIBLING, not a descendant, so the name costs nothing here -- but note that
   a view-transition-name is a stacking context, and check that again if the
   sky is ever moved inside the card. */
body.sas-login-page .sas-login-shell { view-transition-name: sas-vt-signin-card; }

/* Entering the app is a bigger event than changing pane, and wants to be seen
   as one. The card's exit is shorter than the arrival so it is gone well
   before the Board finishes resolving. */
html:active-view-transition-type(sas-signin) {
  --sas-vt-in: 560ms;
  --sas-vt-out: 300ms;
}

html:active-view-transition-type(sas-signin)::view-transition-old(root) {
  animation: sas-vt-fade-out var(--sas-vt-out) var(--sas-vt-ease-in) both;
}
html:active-view-transition-type(sas-signin)::view-transition-new(root) {
  animation: sas-vt-fade-in var(--sas-vt-in) var(--sas-vt-ease-out) both;
}

/* The card is an old-only group, so it paints ABOVE root and the Board is
   revealed through it as it goes. It recedes rather than sliding: the card is
   a window you were looking through, and a window does not travel. */
html:active-view-transition-type(sas-signin)::view-transition-old(sas-vt-signin-card) {
  animation: sas-vt-signin-card-out var(--sas-vt-signin-out) var(--sas-vt-ease-in) both;
}
@keyframes sas-vt-signin-card-out {
  to {
    opacity: 0;
    transform: scale(var(--sas-vt-signin-scale)) translateY(var(--sas-vt-signin-drift));
  }
}

/* The toolbar arrives a beat after the page it belongs to, so the app reads
   as assembling rather than as one flat fade. Section 4 already gave it a
   fade; this only adds the delay, and it is short enough to stay inside the
   root's own arrival. */
html:active-view-transition-type(sas-signin)::view-transition-new(sas-vt-chrome):only-child {
  animation:
    sas-vt-fade-in var(--sas-vt-in) var(--sas-vt-ease-out) var(--sas-vt-signin-lag) both,
    sas-vt-rise var(--sas-vt-in) var(--sas-vt-ease-out) var(--sas-vt-signin-lag) both;
}

/* --- 6. reduced motion --------------------------------------------------- */

/* Displacement goes; a short cross-fade stays, because the alternative for
   these users is the hard flash of an unstyled page swap, which is worse.
   Durations are overridden rather than zeroed so the animations still
   resolve. Anything added here must use the tokens -- a hardcoded duration
   ignores the preference, and that includes the two inside
   `sas-vt-signin-card-out`, which is why the scale and the drift are tokens
   and not literals. KEEP THIS BLOCK LAST. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --sas-vt-in: 140ms;
    --sas-vt-out: 90ms;
    --sas-vt-rise: 0px;
    --sas-vt-signin-out: 120ms;
    --sas-vt-signin-scale: 1;
    --sas-vt-signin-drift: 0px;
    --sas-vt-signin-lag: 0ms;
  }
  html:active-view-transition-type(sas-signin) {
    --sas-vt-in: 140ms;
    --sas-vt-out: 90ms;
  }
}
/* SAS-VT-END ============================================================= */
