.toast{animation:fadeInOut 3s ease-in-out forwards}
@keyframes fadeInOut{0%{opacity:0;transform:translateY(-10px)}10%{opacity:1;transform:translateY(0)}80%{opacity:1}100%{opacity:0}}
/* Sidebar collapse — state lives on <html class="nav-collapsed">, restored from
   localStorage before paint. All collapse visuals are driven by CSS off that class
   (no per-element JS), so the rail is correct on first render.
   .nav-label/.nav-when-expanded/.nav-when-collapsed/.nav-toggle-btn are unique to the
   sidebar's own nav_leaf macro + toggle button (nowhere else in the templates uses
   them), so those rules are safe unscoped. .nav-leaf is NOT unique — settings/_shell.html
   and wiki.html both reuse the class for their own left-aligned sub-nav rows (snav()/its
   wiki twin) — so an unscoped html.nav-collapsed .nav-leaf rule centred those sub-navs
   too the moment the SIDEBAR collapsed, even though they were never in the collapsed
   rail (Philip 2026-08-01, spotted on /settings/skill-notes). Scoped to .nav-aside
   (the sidebar element itself) so only the sidebar's own leaves/toggle are affected.

   MOTION (2026-09-27, Philip: "jumpy/laggy specially when you fold it"). Both states
   are ONE layout, and a row's box is the same in both. The collapsed rail used to be
   a different flex layout (centred rows, zero padding) applied on the click frame while
   only the width transitioned, so every icon leapt 87.5px right into the middle of a
   rail that was still 240px wide, then slid back as it narrowed; unfolding snapped the
   icons 4.5px and revealed the labels through an ellipsis. Now:
     - rows keep their left alignment and their padding, so the icons do not move at all.
       The first version slid padding 12px -> 7.5px, which still shifted every icon 4.5px
       left on fold (Philip, 2026-10-02: "would be nice if they are static placed"). The
       one padding (px-2) is chosen to centre the icon in the folded rail: (56px rail -
       1px border - 16px row margin - 24px icon box) / 2 = 7.5px, within half a pixel of
       8px, so it is coupled to all four of those;
     - labels stay in flow and fade: opacity, then visibility once the fade has run.
       visibility, not opacity alone, keeps a folded link's accessible name coming from
       its title attribute and keeps invisible words out of find-in-page. They clip
       instead of ellipsizing while the rail opens (rail-scoped: the wiki sub-nav's own
       long titles still ellipsize). Staying in flow also means a folded row keeps its
       unfolded height: with a larger browser font the label's line is taller than the
       icon, and the rows used to get shorter on fold, moving the lower icons up by 20-50px;
     - the folded rail shows no scrollbar (it still scrolls by wheel, touch and keyboard).
       It only has one on a very short window, and a space-taking one would narrow the
       rows the centring above assumes, leaving every icon off-centre;
     - reduced motion stops the slide and both label fades; the hover fade stays.
   Pinned by tests/container/test_sidebar_collapse_motion.py. */
.nav-when-collapsed{display:none}
html.nav-collapsed .nav-when-expanded{display:none}
html.nav-collapsed .nav-when-collapsed{display:inline-flex}
html.nav-collapsed .nav-aside{width:3.5rem}
.nav-aside .nav-label{text-overflow:clip;transition:opacity .1s ease 50ms,visibility 0s linear}
html.nav-collapsed .nav-aside .nav-label{opacity:0;visibility:hidden;transition:opacity 75ms ease,visibility 0s linear 75ms}
html.nav-collapsed .nav-aside nav{scrollbar-width:none}
html.nav-collapsed .nav-aside nav::-webkit-scrollbar{display:none}
@media (prefers-reduced-motion: reduce){
  html .nav-aside,html .nav-aside .nav-label,html.nav-collapsed .nav-aside .nav-label{transition:none}
}
/* User-menu chevron rotates while its dropdown is open. */
.menu-chevron{transition:transform .15s ease}
[data-menu-root].is-open .menu-chevron{transform:rotate(180deg)}
/* Scrollbars themed to the chrome instead of the browser default light bar.
   Thumb colour is .dark-gated and uses the REMAPPED slate scale from
   tailwind.config above (navy) — NOT default Tailwind slate — so the bar
   matches the navy chrome exactly. A light fallback is kept so it stays
   correct if light mode is ever enabled. Transparent track + inset
   (border-clipped) thumb so it reads as a slim, rounded bar. */
*{scrollbar-width:thin;scrollbar-color:rgb(var(--outline)) transparent}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track,::-webkit-scrollbar-corner{background:transparent}
::-webkit-scrollbar-thumb{background:rgb(var(--outline));border-radius:9999px;border:2px solid transparent;background-clip:padding-box}
::-webkit-scrollbar-thumb:hover{background:rgb(var(--surface-container-highest))}

/* App-like navigation: animate ONLY the content area; keep chrome (sidebar,
   top bar) still by suppressing the root cross-fade. */
@media (prefers-reduced-motion: no-preference) {
  main { view-transition-name: bjoern-content; }  /* assumes a single <main> per page */
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none; }
  ::view-transition-old(bjoern-content) {
    animation: 140ms cubic-bezier(0.4, 0, 1, 1) both vt-content-out;
  }
  ::view-transition-new(bjoern-content) {
    animation: 200ms cubic-bezier(0, 0, 0.2, 1) both vt-content-in;
  }
  @keyframes vt-content-out { to { opacity: 0; transform: translateY(2px); } }
  @keyframes vt-content-in { from { opacity: 0; transform: translateY(4px); } }
}
/* Reduced-motion: suppress ALL transition animation -> instant swap, no fade. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}
