/* Motion: short and calm, from 120 to 400 ms, never bouncy. Loaded last, after contrast.css.

   A section, a fold or a Help question opens and closes with its height and a fade (180 ms); a
   window fades and grows from 0.97 (160 ms) over a backdrop that fades in; a toast slides up as
   it fades in; what a button opens (a row's numbers on Rotation and on Numbers, a batch's
   numbers, Show all 12, a fold, the Overview's vendors, Filter, the phone's More menu) fades in;
   buttons press in to 0.98 and their hover colours change over 120 ms.

   Then what tells you something changed (js/motion.js starts most of it): a new screen fades in
   and rises 8 px; a figure that changes is lit for a moment; bars grow and shrink to their new
   size (300 ms); a number tile and a batch box lift 2 px under the pointer; a number copied on
   tap flashes green; the Live badge and the lamps of what is running pulse slowly; the selected
   highlight of a segmented control and of the sidebar slides to the new choice (220 ms); new rows
   slide in, never on a page's first draw; and a loading line shimmers gently.

   What a button opens fades only while <html> has data-opening, which app.js sets for a moment
   when such a button is pressed. Without it, a strip remembered open would fade in each time the
   page is drawn, and a row moved by a re-render would fade in again.

   With "reduce motion" on the device none of this applies: every rule is inside the media query
   below, styles.css switches off the rest, and js/motion.js does nothing. A browser without
   ::details-content or interpolate-size opens a section at once, as before. */

@media (prefers-reduced-motion: no-preference) {
  /* ------------------------------------------------------------ sections */
  @supports (interpolate-size: allow-keywords) and selector(::details-content) {
    details::details-content {
      interpolate-size: allow-keywords;
      transition:
        block-size 180ms ease-out,
        opacity 180ms ease-out,
        content-visibility 180ms ease-out allow-discrete;
    }
    /* Closed: no height and clipped, so it shrinks inside its edge as it closes. */
    details:not([open])::details-content {
      block-size: 0;
      opacity: 0;
      overflow: clip;
    }
    /* Opening: clipped only while it grows. Open, nothing is clipped, so a focus ring or a
       "What is this?" button at its edge shows and takes taps as before. */
    details[open]::details-content {
      animation: motion-clip 180ms;
    }
    /* Help's "In this article" on a phone opens by itself when a long article is drawn, and
       closes itself as one of its headings is pressed, while the page scrolls to that heading.
       It opens and closes at once: nothing grows in as the page loads, and the heading lands
       where it should (a fold still shrinking would move it after the scroll has set off). */
    details.help-toc-phone::details-content {
      transition: none;
      animation: none;
    }
  }

  /* The chevron of a section, a fold or a Help question turns with it. */
  details > summary::before,
  details > summary::after {
    transition: transform 180ms ease-out, margin-top 180ms ease-out;
  }
  details.help-toc-phone > summary::before {
    transition: none;
  }

  /* ------------------------------------------------------------- windows */
  dialog[open] {
    transition: opacity 160ms ease-out, scale 160ms ease-out;
  }
  dialog::backdrop {
    transition: opacity 160ms ease-out;
  }
  @starting-style {
    dialog[open] { opacity: 0; scale: 0.97; }
    dialog[open]::backdrop { opacity: 0; }
  }
  /* On a phone Add numbers is a sheet on the bottom edge: it grows from that edge. */
  @media (max-width: 719px) {
    dialog.numbers-dlg { transform-origin: 50% 100%; }
  }

  /* "What is this?" fades in where it opens. */
  .popover {
    transition: opacity 140ms ease-out;
  }
  @starting-style {
    .popover { opacity: 0; }
  }

  /* --------------------------------------------------------------- toasts */
  .toast {
    transition: opacity 180ms ease-out, translate 180ms ease-out;
  }
  @starting-style {
    .toast { opacity: 0; translate: 0 8px; }
  }

  /* The Not saved bar rises into place when the first change is typed. The alert form's bar is
     part of its page, there from the start, so it does not come in as the page is drawn. */
  .form-bar-host .form-bar {
    transition: opacity 160ms ease-out, translate 160ms ease-out;
  }
  @starting-style {
    .form-bar-host .form-bar { opacity: 0; translate: 0 8px; }
  }

  /* ----------------------------------------------- what a button opens */
  .rotation-tr-nums,
  .rotation-bnums,
  .numbers-w-nums,
  .strip-wrap .tile-extra,
  .rotation-unit,
  .rotation-list,
  .ov-vendors-body,
  .cust-quiet,
  .activity-filters,
  .status-chips,
  .alerts-level-box {
    transition: opacity 160ms ease-out;
  }
  .more-menu {
    transition: opacity 160ms ease-out, translate 160ms ease-out;
  }
  .more-scrim {
    transition: opacity 160ms ease-out;
  }
  @starting-style {
    :root[data-opening] :is(
      .rotation-tr-nums,
      .rotation-bnums,
      .numbers-w-nums,
      .strip-wrap.open .tile-extra,
      .rotation-unit,
      .rotation-list,
      .ov-vendors.open .ov-vendors-body,
      .ov-customers.show-all .cust-quiet,
      .activity-filters,
      .status-strip.open .status-chips,
      .alerts-level-box,
      .more-scrim
    ) { opacity: 0; }
    :root[data-opening] .more-menu { opacity: 0; translate: 0 6px; }
  }

  /* -------------------------------------------------------------- buttons */
  /* Hover colours change over 120 ms, and a press sinks the button to 0.98. scale is its own
     property, so a button's transform (the number strip's roll) is left as it is. */
  button,
  .btn,
  .btn-link,
  .tab,
  .side-link,
  .bar-link,
  .more-menu a,
  .tabs a,
  details > summary {
    transition-property: color, background-color, border-color, text-decoration-color, box-shadow, scale;
    transition-duration: 120ms;
    transition-timing-function: ease-out;
  }
  :is(button, .btn):not(:disabled, [aria-disabled="true"]):active {
    scale: 0.98;
  }

  /* ---------------------------------------------------------------- bars */
  /* A bar takes its size from --pct (ui.js applyBars): a new size grows or shrinks to it. A bar
     drawn for the first time has no size before, so it is simply there. */
  .tile-use,
  .meter-fill,
  .numbers-seg,
  .numbers-share-fill,
  .rotation-num-fill {
    transition: width 300ms cubic-bezier(0.2, 0.7, 0.2, 1);
  }

  /* -------------------------------------------------------- hover lifts */
  @media (hover: hover) {
    .tile,
    .rotation-batch {
      transition: translate 160ms ease-out, box-shadow 160ms ease-out, border-color 160ms ease-out;
    }
    .tile:hover,
    .rotation-batch:hover {
      translate: 0 -2px;
      border-color: var(--rule-strong);
      box-shadow: 0 2px 0 var(--rule);
    }
    .rotation-batch.now:hover {
      border-color: var(--accent);
      box-shadow: inset 0 0 0 1px var(--accent), 0 2px 0 var(--rule);
    }
  }

  /* ------------------------------------------------- what is running */
  /* The Live badge and the lamp of each vendor or trunk group that runs, slowly. */
  .mode-badge.live .lamp,
  .rotation-state .lamp.ok,
  .rotation-state .lamp.test {
    animation: motion-pulse 2400ms ease-in-out infinite;
  }

  /* ------------------------------------------------------- loading lines */
  .loading {
    animation: motion-shimmer 1600ms ease-in-out infinite;
  }

  /* --------------------------------------------- the sliding highlight */
  /* js/motion.js sets where the chosen part is on the control (--slide-pos and --slide-size, and
     for a segmented control that wraps, --slide-top and --slide-height), and --slide-on once it
     has. Until then, or without it, the chosen part draws its own highlight. */
  .seg,
  .side-nav ul {
    position: relative;
    isolation: isolate;
  }
  .seg[style*="--slide-on"]::before,
  .side-nav ul[style*="--slide-on"]::before {
    content: "";
    position: absolute;
    z-index: -1;
    background: var(--accent-tint);
    transition:
      translate var(--slide-t, 0ms) cubic-bezier(0.2, 0.7, 0.2, 1),
      width var(--slide-t, 0ms) cubic-bezier(0.2, 0.7, 0.2, 1),
      height var(--slide-t, 0ms) cubic-bezier(0.2, 0.7, 0.2, 1);
  }
  .seg[style*="--slide-on"]::before {
    top: 0;
    left: 0;
    width: var(--slide-size);
    height: var(--slide-height);
    translate: var(--slide-pos) var(--slide-top);
  }
  .seg[style*="--slide-on"] button[aria-pressed="true"] {
    background: none;
  }
  /* The sidebar's current page is a solid block (contrast.css): the block slides, and the words
     of the page left and the page chosen change colour as it passes, so they never sit light on
     light. */
  .side-nav ul[style*="--slide-on"]::before {
    top: 0;
    left: 0;
    right: 0;
    height: var(--slide-size);
    translate: 0 var(--slide-pos);
    border-radius: 4px;
    background: var(--side-on-fill, var(--side-on, var(--accent-tint)));
  }
  .side-nav ul[style*="--slide-on"] .side-link[aria-current="page"] {
    background: none;
  }
  .side-nav ul[style*="--slide-on"] .side-link,
  .side-nav ul[style*="--slide-on"] .side-link .glyph {
    transition: color var(--slide-t, 0ms) cubic-bezier(0.2, 0.7, 0.2, 1), background-color 120ms ease-out;
  }
}

@keyframes motion-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}

@keyframes motion-shimmer {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

@keyframes motion-clip {
  from, to { overflow: clip; }
}
