/* Motion — the ONE rule for everything that opens, closes, moves or fades.
 *
 * Loaded by every page, visitor and admin alike, before its own stylesheet,
 * so these tokens are the only place a duration or a curve is decided. A
 * `transition` anywhere else names a token, never a number: a new panel that
 * opens at 300 ms beside one that opens at 250 ms is how a product starts to
 * feel assembled rather than designed. JS that has to wait for a CSS motion
 * (remove an element after its fade, start the next step of a hand-over)
 * reads the same token through `motionMs()` in src/web/motion.js.
 *
 * FOUR DURATIONS, picked by what moves — not by taste per component:
 *   quick  150 ms  state feedback in place: hover, press, focus, a colour or a
 *                  border changing. Nothing appears or travels.
 *   base   250 ms  something appears, disappears or folds in place: a popover,
 *                  a fold-out, a fade, a label, the modal, a panel's turn.
 *   large  450 ms  a surface travels across the frame: the flats drawer, a
 *                  sliding panel, cards arriving, the loading screen leaving.
 *   scene  900 ms  the whole view hands over to another: the drawing receding
 *                  into the neighbourhood map and back.
 *
 * FOUR CURVES, picked by the direction of the motion:
 *   feedback  hover/press — the browser's `ease`, quick and unremarkable.
 *   standard  from one state to another, both on screen: fades, cross-fades,
 *             folds, a turn. Symmetric, so opening and closing match.
 *   enter     something arriving or opening out: fast out, settles softly.
 *   exit      something leaving for good: gathers speed as it goes.
 *
 * Choreography is built FROM the tokens: a step that waits for another waits
 * exactly that token (`transition-delay: var(--ff-motion-base)`), so the two
 * can never drift apart when a token is tuned.
 *
 * NOT covered, on purpose: camera motion is content, not interface — the
 * panorama engine's view, the map's flights, the drone clips — and has its
 * own physics. Attention loops (a pulse, a flash, a spinner) are keyframes with
 * their own rhythm, but every one of them stops under reduced motion.
 *
 * Reduced motion: the tokens collapse to 1 ms (not 0 — `transitionend` still
 * fires, and a JS wait reading the token shrinks with them), so a visitor who
 * asked the system for less motion gets it everywhere at once.
 */
:root {
  --ff-motion-quick: 150ms;
  --ff-motion-base: 250ms;
  --ff-motion-large: 450ms;
  --ff-motion-scene: 900ms;

  --ff-ease-feedback: ease;
  --ff-ease-standard: cubic-bezier(0.45, 0, 0.55, 1);
  --ff-ease-enter: cubic-bezier(0.22, 1, 0.36, 1);
  --ff-ease-exit: cubic-bezier(0.4, 0, 1, 1);
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --ff-motion-quick: 1ms;
    --ff-motion-base: 1ms;
    --ff-motion-large: 1ms;
    --ff-motion-scene: 1ms;
  }
}

/* A fold-out: content that opens downwards and pushes what is below it.
 *
 * The grid-rows trick, so no height is measured in JS: the row goes 0fr → 1fr
 * and the content's own height is what it animates to. The element stays in
 * the layout while closed (a `hidden` would cut the motion off), so the JS
 * side — `setFold()` in src/web/motion.js — makes the closed content `inert`,
 * which takes it out of the tab order and the accessibility tree alike.
 *
 * Markup: <div class="ff-fold"><div class="ff-fold-body">…</div></div>
 */
.ff-fold {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transition:
    grid-template-rows var(--ff-motion-base) var(--ff-ease-standard),
    opacity var(--ff-motion-base) var(--ff-ease-standard);
}

.ff-fold.is-open {
  grid-template-rows: 1fr;
  opacity: 1;
}

.ff-fold > .ff-fold-body {
  min-height: 0;
  overflow: hidden;
}
