/* Mamma Donella: motion (owner: motion, .orchestration/site-2026-10-09/contrato.md «Ganchos de movimento»).
   Linked by the head part after site.css and the page CSS; site.js does the rest. Values are the motion tokens
   of tokens.css (brand/brand-brief.md, the direction's «Movimento» line, local-business-studio/reference/
   motion.md). Only transform (translate, rotate), opacity and clip-path move. The signature is the seal: the
   terracota disc on every page change, the dashed ring turning, the frames hanging, the chalk writing itself.
   Terracota never carries anything black or dark grey: the disc opens on castanho, cream or paper only.

   The guard. An element starts hidden only under html.js (the head swaps no-js for js), with
   prefers-reduced-motion: no-preference and outside html.quieto. site.js adds html.movimento when it runs
   and reveals each element once; it adds html.quieto instead when motion is reduced, when the browser lacks
   IntersectionObserver or the Web Animations API, or when it ran so late that the safety net below already
   showed the page. If site.js never runs, the safety net fades everything in at 2,6 s. Reduced motion
   hides nothing; it keeps a short crossfade between pages.

   What moves, where and why (find-animation-opportunities, 9 October 2026):
   1. Page change, every navigation between the site's pages: the old page's words leave first (90 ms), the
      page goes into the terracota field (160 ms), and the new page opens in a disc from the centre (380 ms);
      its words (the whole <main>) come in only once the disc is nearly open. The header (cabecalho) and the
      phone bar (barra) hold still above it. Reduced motion: a 120/160 ms crossfade.
   2. Arrival: the page title rises line by line through its mask, then the lead and the actions (site.js,
      timed after the disc when there is one).
   3. data-rv: sobe (lift), lado (from the nearer side), mascara (lines through their mask, or a curtain from
      the top for a photo inside its frame), lista and data-rv-kids (children in turn, 55 ms apart, capped at
      440 ms), pendura (the frame body from 0° and 8 px up to its own tilt, 90 ms apart; no opacity, so a
      first-screen photo is painted from the first frame).
   4. data-giz: the chalk writes itself, rule by rule, clip-path from the left edge of each rule's text.
   5. data-selo-gira: the ring turns 24° into place when the seal enters, and with the scroll while it
      crosses the screen (scroll-driven, compositor only); off screen it does not move.
   6. Section titles with lines and no hook of their own take the mascara reveal.
   7. Press: the arrow of a .ligacao nudges forward under the finger (the buttons' press: site.css). */

/* 1. Page change ------------------------------------------------------------------------------------- */

/* The type «pagina» marks a navigation between documents; a same-document transition (none is planned)
   never gets the disc. Only with scripting on: without JS no pageswap handler runs, and Chromium driven by
   automation stalls on cross-document transitions (tools/motion/README.md). */
@media (scripting: enabled) {
  @view-transition { navigation: auto; types: pagina; }
}

/* The page's words travel apart from the page so they can leave first and arrive last. Named only during a
   page change; site.js renames the old page's <main> «conteudo-sai» at pageswap (or leaves it unnamed when a
   dialog covers it), so the old and the new never pair and slide. */
:root:active-view-transition-type(pagina) main { view-transition-name: conteudo; }

/* The disc is the new page's own ground: castanho when it opens on a castanho field, cream otherwise. */
:root:active-view-transition-type(pagina) body:has(> main:is(.campo--castanho, .md-on-dark)),
:root:active-view-transition-type(pagina) body:has(> main > :first-child:is(.campo--castanho, .md-on-dark)) {
  background-color: var(--md-surface-main);
}

@media (prefers-reduced-motion: no-preference) {
  :root:active-view-transition-type(pagina)::view-transition-group(root) {
    background-color: var(--md-terracotta);
  }
  /* The old ground fades into the terracota just after the words have gone. */
  :root:active-view-transition-type(pagina)::view-transition-old(root) {
    mix-blend-mode: normal;
    animation: md-sai var(--md-dur-page-out) var(--md-ease-out) 50ms both;
  }
  /* The new page opens in a disc from the centre. 70,7 % of the reference radius reaches the corners; the
     disc runs on to 78 %, so the corners are covered at 90 % of its course, before the words come in. */
  :root:active-view-transition-type(pagina)::view-transition-new(root) {
    mix-blend-mode: normal;
    animation: md-disco var(--md-dur-page-in) var(--md-ease-out) 120ms both;
  }
  /* The words: out in 90 ms, before the terracota shows; in once the disc covers the corners. */
  :root:active-view-transition-type(pagina)::view-transition-old(conteudo-sai),
  :root:active-view-transition-type(pagina)::view-transition-old(conteudo) {
    animation: md-sai 90ms var(--md-ease-out) both;
  }
  :root:active-view-transition-type(pagina)::view-transition-new(conteudo) {
    animation: md-entra 180ms var(--md-ease-out) 270ms both;
  }
}
/* Without site.js on the old page both <main>s carry the same name: the old words go at once instead of
   sliding to where the new ones are. */
:root:active-view-transition-type(pagina)::view-transition-old(conteudo) { display: none; }
:root:active-view-transition-type(pagina)::view-transition-group(conteudo) { animation: none; }

@keyframes md-disco {
  from { clip-path: circle(0% at 50% 50%); }
  to { clip-path: circle(78% at 50% 50%); }
}
@keyframes md-sai { to { opacity: 0; } }
@keyframes md-entra { from { opacity: 0; } }

/* The header and the bar are the same on every page: they hold still above the disc. No crossfade: the new
   picture lies on the old one, both opaque. The group clips them, so a header that grows or shrinks between
   two pages (the demo strip has a second line on /reservar and /grupos) moves its lower edge instead of
   showing the old one under the new. When site.js left the old header unnamed (scrolled away, or under the
   open menu), the new one is there from the first frame. */
::view-transition-group(cabecalho),
::view-transition-group(barra) {
  overflow: clip;
  animation-duration: var(--md-dur-page-in);
  animation-timing-function: var(--md-ease-out);
}
::view-transition-old(cabecalho), ::view-transition-new(cabecalho),
::view-transition-old(barra), ::view-transition-new(barra) {
  animation: none;
  mix-blend-mode: normal;
}
/* The bar leaves for a booking step (data-sem-barra) and comes back after one, as a drawer. */
@media (prefers-reduced-motion: no-preference) {
  ::view-transition-group(barra):only-child { overflow: visible; }
  ::view-transition-old(barra):only-child { animation: md-barra-sai var(--md-dur-page-out) var(--md-ease-out) both; }
  ::view-transition-new(barra):only-child { animation: md-barra-entra var(--md-dur-drawer-in) var(--md-ease-drawer) 250ms both; }
}
@keyframes md-barra-sai { to { transform: translateY(100%); } }
@keyframes md-barra-entra { from { transform: translateY(100%); } }

@media (prefers-reduced-motion: reduce) {
  :root:active-view-transition-type(pagina)::view-transition-group(root) { background-color: var(--md-surface); }
  ::view-transition-old(root) { animation: md-sai var(--md-dur-reduced-out) ease both; }
  ::view-transition-new(root) { animation: md-entra var(--md-dur-reduced-in) ease both; }
  ::view-transition-old(conteudo-sai) { animation: md-sai var(--md-dur-reduced-out) ease both; }
  ::view-transition-new(conteudo) { animation: md-entra var(--md-dur-reduced-in) ease both; }
  ::view-transition-group(cabecalho), ::view-transition-group(barra) { animation-duration: var(--md-dur-reduced-in); }
  ::view-transition-old(barra):only-child { animation: md-sai var(--md-dur-reduced-out) ease both; }
  ::view-transition-new(barra):only-child { animation: md-entra var(--md-dur-reduced-in) ease both; }
}

/* 2 to 6. Arrival and reveals: hidden states, only with JavaScript and full motion -------------------- */

/* A line hook outside .titulo (site.css sets it there) is a line too: a block with its words in one box, so the
   words can move through the mask. Same layout with and without JavaScript; zero specificity, so a page can
   still place its lines. */
:where(.h2, .h3, .h4, [data-rv="mascara"], [data-rv-kids="mascara"] > *) .ln { display: block; }
:where(.h2, .h3, .h4, [data-rv="mascara"], [data-rv-kids="mascara"] > *) .ln > span { display: inline-block; }

@media (prefers-reduced-motion: no-preference) {
  /* The mask of a title line: a little room above for the accents (Á, É) and below for the tails (ç, p, g),
     without touching the layout. */
  html.js:not(.quieto) :is(.titulo, .h2, [data-rv="mascara"]) .ln { clip-path: inset(-0.24em -0.5em -0.3em -0.5em); }

  /* The page title, then the lead and the actions that follow it (site.js times them after the disc). */
  html.js:not(.quieto) h1.titulo:not([data-rv], .is-in) .ln > span { translate: 0 calc(100% + 0.6em); }
  html.js:not(.quieto) h1.titulo:not([data-rv]) ~ :is(.lead, .acoes):not([data-rv], .is-in) { opacity: 0; }

  /* Section titles with lines and no hook take the mascara reveal; so does any data-rv="mascara" with lines. */
  html.js:not(.quieto) :is(.h2, .titulo):not(h1, [data-rv], :is([data-rv], [data-rv-kids], [data-giz]) *, .is-in) .ln > span,
  html.js:not(.quieto) [data-rv="mascara"]:not(.is-in) .ln > span,
  html.js:not(.quieto) [data-rv-kids="mascara"] > :not(.is-in) .ln > span { translate: 0 calc(100% + 0.6em); }

  /* mascara without lines: a photo (inside its figure or frame) or a block behind a curtain that keeps a
     thin strip at the top, so a first-screen photo is painted from the first frame. */
  html.js:not(.quieto) :is([data-rv="mascara"], [data-rv-kids="mascara"] > *):not(.is-in, :has(.ln)):is(figure, .md-frame, picture) img,
  html.js:not(.quieto) :is([data-rv="mascara"], [data-rv-kids="mascara"] > *):not(.is-in, :has(.ln), figure, .md-frame, picture) { clip-path: inset(0 0 96% 0); }

  /* sobe, lado and the children of a group: transparent until site.js lifts them in. */
  html.js:not(.quieto) :is([data-rv="sobe"], [data-rv="lado"], [data-rv=""]):not(.is-in),
  html.js:not(.quieto) :is([data-rv-kids=""], [data-rv-kids="sobe"], [data-rv-kids="lado"], [data-rv="lista"]:not([data-rv-kids])) > :not(.is-in, [data-rv]:not([data-rv="lista"]), :only-child:is(ul, ol)),
  html.js:not(.quieto) :is([data-rv-kids=""], [data-rv-kids="sobe"], [data-rv-kids="lado"], [data-rv="lista"]:not([data-rv-kids])) > :only-child:is(ul, ol) > :not(.is-in) { opacity: 0; }

  /* pendura: the frame body (or a chalkboard) waits straight and 8 px up, fully painted; site.js lets it
     drop onto its nail and take its tilt. */
  html.js:not(.quieto) [data-rv="pendura"]:not(.is-in) .md-frame__body,
  html.js:not(.quieto) [data-rv="pendura"]:not(.is-in, :has(.md-frame__body)):is(.md-board, .md-frame__body),
  html.js:not(.quieto) [data-rv-kids="pendura"] > :not(.is-in) .md-frame__body,
  html.js:not(.quieto) [data-rv-kids="pendura"] > .md-board:not(.is-in) {
    transform: translateY(calc(-1 * var(--md-frame-drop))) rotate(0deg);
  }

  /* data-giz: the rules wait unwritten on the board. */
  html.js:not(.quieto) [data-giz]:not(.is-in) .md-board__line { clip-path: inset(0 100% 0 0); }

  /* The safety net: if site.js never adds html.movimento (or html.quieto), everything fades in at 2,6 s. */
  html.js:not(.movimento, .quieto) h1.titulo:not([data-rv]) .ln > span,
  html.js:not(.movimento, .quieto) h1.titulo:not([data-rv]) ~ :is(.lead, .acoes),
  html.js:not(.movimento, .quieto) :is(.h2, .titulo, [data-rv="mascara"], [data-rv-kids="mascara"] > *) .ln > span,
  html.js:not(.movimento, .quieto) :is([data-rv="mascara"], [data-rv-kids="mascara"] > *):is(figure, .md-frame, picture) img,
  html.js:not(.movimento, .quieto) :is([data-rv="mascara"], [data-rv-kids="mascara"] > *):not(figure, .md-frame, picture),
  html.js:not(.movimento, .quieto) :is([data-rv="sobe"], [data-rv="lado"], [data-rv=""]),
  html.js:not(.movimento, .quieto) :is([data-rv-kids], [data-rv="lista"]) > *,
  html.js:not(.movimento, .quieto) :is([data-rv-kids], [data-rv="lista"]) > :only-child:is(ul, ol) > *,
  html.js:not(.movimento, .quieto) [data-giz] .md-board__line {
    animation: md-rede var(--md-dur-reduced-in) ease 2.6s both;
  }
  html.js:not(.movimento, .quieto) :is([data-rv="pendura"], [data-rv-kids="pendura"] > *) .md-frame__body,
  html.js:not(.movimento, .quieto) :is([data-rv="pendura"], [data-rv-kids="pendura"] > *):is(.md-frame__body) {
    animation: md-rede-pendura var(--md-dur-reduced-in) ease 2.6s both;
  }
  html.js:not(.movimento, .quieto) :is([data-rv="pendura"], [data-rv-kids="pendura"] > *):is(.md-board) {
    animation: md-rede-quadro var(--md-dur-reduced-in) ease 2.6s both;
  }
}
@keyframes md-rede { to { opacity: 1; translate: none; clip-path: none; } }
@keyframes md-rede-pendura { to { transform: rotate(var(--md-tilt, 0deg)); } }
@keyframes md-rede-quadro { to { transform: rotate(var(--md-tilt, -0.6deg)); } }
@media (max-width: 480px) {
  @keyframes md-rede-quadro { to { transform: rotate(var(--md-tilt-phone, -0.4deg)); } }
}

/* 5. The ring of a large seal: it pivots on its own centre (an <img> or the ring's <g> inside the seal's
   <svg>). Scroll-driven, so it runs on the compositor and stops when the seal is off screen or the page is
   still: 60° from entering at the bottom to leaving at the top. The view timeline sits on the <svg> (an SVG
   group has no box of its own to be the subject). Where scroll timelines are missing, only the entry turn
   (site.js) plays. */
[data-selo-gira] { transform-box: fill-box; transform-origin: 50% 50%; }
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    html:not(.quieto) svg:has([data-selo-gira]),
    html:not(.quieto) [data-selo-gira]:not(svg *) { view-timeline: --md-selo block; }
    html:not(.quieto) [data-selo-gira] {
      animation: md-selo-gira linear both;
      animation-timeline: --md-selo;
      animation-range: cover;
    }
  }
}
@keyframes md-selo-gira { from { rotate: -30deg; } to { rotate: 30deg; } }

/* 7. Press: the arrow moves the way the link goes, under the finger as under the mouse. */
@media (prefers-reduced-motion: no-preference) {
  .ligacao:active .ico { transform: translateX(3px); }
}

@media print {
  html.js :is([data-rv], [data-rv-kids] > *, [data-rv-kids] > :only-child > *, [data-rv="lista"] > *, [data-rv="lista"] > :only-child > *,
    [data-rv] img, .ln, .ln > span, [data-giz] .md-board__line, h1.titulo ~ .lead, h1.titulo ~ .acoes) {
    opacity: 1 !important; translate: none !important; clip-path: none !important; animation: none !important;
  }
}
