/* Knashboard motion utility layer (WB-014, groom label KM-MOTION).
 *
 * The static page is the base: nothing in this sheet hides, offsets,
 * or dims any element on its own. Every animation rule is scoped
 * under `html.km-anim`, a class the module's JS adds only when
 * motion is allowed — so the no-JS state, the JS-failed state, and
 * the reduced-motion state are the complete static page, standing
 * before any animation lands. Tokens are locally scoped to the stat
 * components and header-band surfaces that consume them (the census
 * of record: the site's standing surfaces are not restyled).
 */

.kpi, .tile, .card, .km-block {
  /* The --km-settle-* pair is the CSS-side record of the ruled
   * durations; the module's SETTLE_MS table is the operative copy,
   * and the battery censuses that the two agree. */
  --km-settle-common: 700ms;
  --km-settle-exceptions: 1400ms;
  --km-enter-dur: 220ms;
  --km-rise: 6px;
  --km-stagger: 30ms;
}

/* The true value's carrier during a settle: present to assistive
 * technology, invisible on screen. */
.km-sr {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Settling numbers never jitter: tabular numerals, always. */
[data-km-settle] { font-variant-numeric: tabular-nums; }

/* Staggered entrances, the direction's grammar: fade + 6px rise,
 * 220 ms, stagger at most 30 ms, index capped by the module at 8. */
html.km-anim .km-enter {
  animation: km-in var(--km-enter-dur, 220ms) ease-out both;
  animation-delay: calc(var(--km-i, 0) * var(--km-stagger, 30ms));
}
@keyframes km-in {
  from { opacity: 0; transform: translateY(var(--km-rise, 6px)); }
  to { opacity: 1; transform: none; }
}

/* The register table's rows are never staggered: the whole block
 * fades once, as one. */
html.km-anim .km-block {
  animation: km-fade var(--km-enter-dur, 220ms) ease-out both;
}
@keyframes km-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Reduced motion, the CSS signal: the complete static state.
 * (The module's JS check is the second signal; either suffices.) */
@media (prefers-reduced-motion: reduce) {
  .km-enter, .km-block { animation: none !important; }
}

/* --- WB-016 (KM-TRANSITIONS): the persistent header band and
 * cross-document view transitions. Progressive enhancement in the
 * direction's three layers: the shared band below (layer 1), the
 * @view-transition opt-in where the browser supports it (layer 2),
 * and nothing at all where it does not (layer 3 — the ordinary
 * instant load, complete and unpenalized). No router, no
 * fetch-and-swap, no history manipulation exists anywhere in this
 * layer; navigation is never delayed for an animation. CSS-only:
 * pages with no other motion JS carry these rules and nothing
 * else. */

/* The one header band form, identical on every page that carries
 * it. The band holds through a transition: it carries the shared
 * view-transition-name, and its transition group does not animate. */
.wb-head {
  view-transition-name: wb-header;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 16px;
  padding: 10px 14px;
  margin-bottom: 22px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
}
.wb-brand {
  color: var(--text);
  font-size: 16px;
  font-weight: 700;
  text-decoration: none;
}
.wb-nav { display: flex; flex-wrap: wrap; gap: 6px 14px; }
.wb-nav a { color: var(--dim); font-size: 14px; text-decoration: none; }
.wb-nav a:hover { color: var(--blue); }

/* The opt-in itself is gated on no-preference: under reduced
 * motion there is no transition at all, not merely an unanimated
 * swap (WB-016 packet review M-2). */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}

/* The outgoing page fades (100 ms), the incoming content rises in
 * the entrance grammar (180 ms); the two run concurrently, so the
 * experienced transition is 180 ms — under the 280 ms bound. */
::view-transition-old(root) {
  animation: km-vt-fade-out 100ms ease-out both;
}
::view-transition-new(root) {
  animation: km-vt-rise-in 180ms ease-out both;
}
::view-transition-group(wb-header),
::view-transition-old(wb-header),
::view-transition-new(wb-header) {
  animation: none;
}
@keyframes km-vt-fade-out {
  to { opacity: 0; }
}
@keyframes km-vt-rise-in {
  /* --km-rise is scoped to the KPI/tile/card surfaces, so on this
   * pseudo-element it resolves through its 6px fallback by design;
   * the value names the entrance grammar's rise (WB-016 review L-4). */
  from { opacity: 0; transform: translateY(var(--km-rise, 6px)); }
  to { opacity: 1; transform: none; }
}

/* Reduced motion disables the transitions entire: the opt-in above
 * stands only under no-preference, and this block stands as the
 * second guard on the animations themselves. The transitions are
 * pure CSS, so the media-query signal is the operative one; it is
 * the same signal the module's JS check reads on the pages that
 * carry motion.js — either form of it suffices. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation: none !important;
  }
}
