/* Sunday in Lisbon: motion (owner: motion). Linked by the head part after site.css and the page CSS;
   site.js does the rest. Values are the motion tokens in tokens.css (brand/brand-brief.md «Movimento»,
   local-business-studio/reference/motion.md). The signature is the wave: a navy wave sweeps the screen on
   every page change, the divider wave draws itself from left to right, and «onda» reveals open behind the
   same wave edge. Only transform (translate, rotate), opacity, clip-path and mask-position move.

   The guard. An element starts hidden only under html.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 already showed the page. If site.js never
   runs, the safety net below fades everything in at 2,6 s. Reduced motion hides nothing.

   What moves, where and why (find-animation-opportunities, 9 October 2026):
   1. Page change, every navigation between the site's pages: the navy wave. The root group turns navy, the
      old page is covered by a wave edge running left to right and the new page is uncovered by a second
      edge 50 ms behind it, so a navy band with wave edges crosses the screen (the brand's sweep masks,
      brand/padroes/README.md). Back through the history it runs right to left. The awning (cabecalho) and
      the phone bar (barra) stay put above it. Reduced motion: a 140 ms crossfade.
   2. Arrival: the page title rises line by line through its mask, then the lead and the actions.
   3. data-onda: the divider wave draws itself, clip-path from the left, once.
   4. data-rv: sobe (lift), lado (from the nearer side), mascara (lines through their mask, or a curtain
      from the top for a photo or a block), onda (opens behind the wave edge, left to right), lista and
      data-rv-kids (children in turn, 55 ms apart, capped at 440 ms).
   5. data-selo-gira: the ring of the seal turns with the scroll (scroll-driven, compositor only); where
      scroll timelines are missing it turns once in 28 s while on screen.
   6. Shell pieces with no hook of their own: section titles with lines (.h2 or a .titulo below the h1), the week of the hours
      (rows in turn) and the marker for now (slides from the start of the day to the hour), the pattern
      bands (unroll from the left), the phone menu's links (in turn, after the drawer).
   7. Press: the arrow of a .ligacao nudges forward under the finger (.btn and .circulo: site.css). */

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

/* The type «pagina» marks a navigation between documents; a same-document transition (a booking step)
   keeps its own motion and never gets the sweep. Only with scripting on: without JS no pageswap or
   pagereveal handler runs, and headless Chromium stopped painting after the transition. */
@media (scripting: enabled) {
  @view-transition { navigation: auto; types: pagina; }
}

@media (prefers-reduced-motion: no-preference) {
  /* The band between the two edges is the navy of the awning. */
  :root:active-view-transition-type(pagina)::view-transition-group(root) {
    background-color: var(--navy);
    animation-duration: var(--dur-page-wave);
  }
  /* The wave edge: one period is 10vw tall (the mask is 300vw wide, 3000 × 100) and the edge sits between
     146,5vw and 153vw of it. At 76,5 % (offset −153vw) the edge is just off the left of the screen, at
     23,25 % (offset −46,5vw) just off the right. Both animations start 120 ms in, where the curve has left
     its slow start: the sweep moves from the first frame. */
  :root:active-view-transition-type(pagina)::view-transition-old(root),
  :root:active-view-transition-type(pagina)::view-transition-new(root) {
    mix-blend-mode: normal;
    -webkit-mask-repeat: repeat-y;
    mask-repeat: repeat-y;
    -webkit-mask-size: 300vw auto;
    mask-size: 300vw auto;
    animation-name: onda-avanca;
    animation-timing-function: var(--ease-in-out);
    animation-fill-mode: both;
  }
  /* The old page keeps what is right of the edge (exit, 460 ms); the new page shows what is left of its
     edge (entry, 560 ms). Same start, so the slower entry edge falls behind the exit edge and the navy
     band between them opens to about a third of the screen in the middle of the crossing. */
  :root:active-view-transition-type(pagina)::view-transition-old(root) {
    -webkit-mask-image: url("/assets/img/padroes/onda-varrimento-sai.svg?v=4e04424d");
    mask-image: url("/assets/img/padroes/onda-varrimento-sai.svg?v=4e04424d");
    animation-duration: calc(var(--dur-page-wave) - 100ms);
    animation-delay: -120ms;
  }
  :root:active-view-transition-type(pagina)::view-transition-new(root) {
    -webkit-mask-image: url("/assets/img/padroes/onda-varrimento-entra.svg?v=c824624a");
    mask-image: url("/assets/img/padroes/onda-varrimento-entra.svg?v=c824624a");
    animation-duration: var(--dur-page-wave);
    animation-delay: -120ms;
  }
  /* Back through the history: the wave runs right to left, so each page keeps the other side. */
  :root:active-view-transition-type(volta)::view-transition-old(root) {
    -webkit-mask-image: url("/assets/img/padroes/onda-varrimento-entra.svg?v=c824624a");
    mask-image: url("/assets/img/padroes/onda-varrimento-entra.svg?v=c824624a");
    animation-name: onda-recua;
  }
  :root:active-view-transition-type(volta)::view-transition-new(root) {
    -webkit-mask-image: url("/assets/img/padroes/onda-varrimento-sai.svg?v=4e04424d");
    mask-image: url("/assets/img/padroes/onda-varrimento-sai.svg?v=4e04424d");
    animation-name: onda-recua;
  }
}
@keyframes onda-avanca {
  from { -webkit-mask-position: 76.5% 0; mask-position: 76.5% 0; }
  to { -webkit-mask-position: 23.25% 0; mask-position: 23.25% 0; }
}
@keyframes onda-recua {
  from { -webkit-mask-position: 23.25% 0; mask-position: 23.25% 0; }
  to { -webkit-mask-position: 76.5% 0; mask-position: 76.5% 0; }
}

/* The awning and the bar are the same on every page: they hold still above the sweep. No crossfade: the
   new picture lies on the old one, both opaque, so the awning never thins out, even in the first frames
   when Chromium has not drawn the new live picture yet (the old one shows through until it has). Only the
   underline of the current page changes. When the awning was scrolled away on the old page (or hidden by
   the open phone menu), site.js leaves it unnamed there, and the new one fades in where it belongs instead
   of travelling from off screen. */
::view-transition-group(cabecalho),
::view-transition-group(barra) {
  animation-duration: var(--dur-enter);
  animation-timing-function: var(--ease-out);
}
::view-transition-old(cabecalho), ::view-transition-new(cabecalho),
::view-transition-old(barra), ::view-transition-new(barra) {
  animation: none;
  mix-blend-mode: normal;
}
::view-transition-new(cabecalho):only-child, ::view-transition-new(barra):only-child {
  animation: pag-esmaece-entra var(--dur-enter) var(--ease-out) both;
}
::view-transition-old(cabecalho):only-child, ::view-transition-old(barra):only-child {
  animation: pag-esmaece-sai var(--dur-exit) var(--ease-out) both;
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root) { animation: pag-esmaece-sai var(--dur-fade-reduced) ease both; }
  ::view-transition-new(root) { animation: pag-esmaece-entra var(--dur-fade-reduced) ease both; }
  ::view-transition-group(cabecalho), ::view-transition-group(barra),
  ::view-transition-old(cabecalho):only-child, ::view-transition-new(cabecalho):only-child,
  ::view-transition-old(barra):only-child, ::view-transition-new(barra):only-child { animation-duration: var(--dur-fade-reduced); }
}
@keyframes pag-esmaece-sai { to { opacity: 0; } }
@keyframes pag-esmaece-entra { from { opacity: 0; } }

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

@media (prefers-reduced-motion: no-preference) {
  /* The page title: CSS keyframes, so it plays from the first frame (the new page is live under the
     sweep) and needs no script. Each line rises from below its own mask; the extra .2em clears the padding
     that keeps the «y» tail and the accents inside the mask. */
  :where(html.js:not(.quieto) h1.titulo:not([data-rv])) .ln > span {
    animation: ln-sobe var(--dur-title-line) var(--ease-out) 60ms both;
  }
  :where(html.js:not(.quieto) h1.titulo:not([data-rv]) .ln:nth-child(2 of .ln)) > span { animation-delay: calc(60ms + var(--stagger-line)); }
  :where(html.js:not(.quieto) h1.titulo:not([data-rv]) .ln:nth-child(3 of .ln)) > span { animation-delay: calc(60ms + 2 * var(--stagger-line)); }
  :where(html.js:not(.quieto) h1.titulo:not([data-rv]) .ln:nth-child(4 of .ln)) > span { animation-delay: calc(60ms + 3 * var(--stagger-line)); }
  :where(html.js:not(.quieto) h1.titulo:not([data-rv]) .ln:nth-child(n + 5 of .ln)) > span { animation-delay: calc(60ms + 4 * var(--stagger-line)); }
  /* Then the lead and the actions that follow the title, unless they carry a hook of their own (site.js
     then starts them after the title). */
  :where(html.js:not(.quieto) h1.titulo:not([data-rv]) ~ .lead):not([data-rv]) { animation: chega var(--dur-enter) var(--ease-out) 320ms both; }
  :where(html.js:not(.quieto) h1.titulo:not([data-rv]) ~ :is(.acoes, p:not(.lead))):not([data-rv]) { animation: chega var(--dur-enter) var(--ease-out) 380ms both; }

  /* Reveals waiting for site.js. Only the start: the movement is the animation site.js plays. A group is
     data-rv-kids, or data-rv="lista" when it is not itself an item of a group (then it is one item). */
  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="lista"]:not(:is([data-rv-kids], [data-rv="lista"]) > *)) > :not(.is-in, :only-child:is(ul, ol, dl)),
  html.js:not(.quieto) :is([data-rv-kids], [data-rv="lista"]:not(:is([data-rv-kids], [data-rv="lista"]) > *)) > :only-child:is(ul, ol, dl) > :not(.is-in) { opacity: 0; }

  /* mascara: lines below their mask; a photo (or a block without lines) behind a curtain that keeps a thin
     strip at the top, so the first-screen photo is painted from the first frame. In a .foto the picture
     moves and the seal on the corner stays. */
  html.js:not(.quieto) [data-rv="mascara"]:not(.is-in) .ln > span { translate: 0 calc(100% + .2em); }
  html.js:not(.quieto) [data-rv="mascara"]:not(.is-in, :has(.ln)):is(.foto, figure) :is(img, video),
  html.js:not(.quieto) [data-rv="mascara"]:not(.is-in, :has(.ln), .foto, figure) { clip-path: inset(0 0 96% 0); }

  /* onda: behind the wave edge, a sliver of it already on the left. */
  html.js:not(.quieto) [data-rv="onda"]:not(.is-in):is(.foto, figure) :is(img, video),
  html.js:not(.quieto) [data-rv="onda"]:not(.is-in, .foto, figure) {
    -webkit-mask: url("/assets/img/padroes/onda-varrimento-entra.svg?v=c824624a") 75.5% 0 / 300% auto repeat-y;
    mask: url("/assets/img/padroes/onda-varrimento-entra.svg?v=c824624a") 75.5% 0 / 300% auto repeat-y;
  }

  /* The divider wave draws itself from the left (a filled shape, so clip-path, not a stroke dash). */
  html.js:not(.quieto) [data-onda]:not(.is-in) { clip-path: inset(0 100% 0 0); }

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

  /* Shell pieces, unless a page hook already moves them or what holds them. */
  html.js:not(.quieto) .horario__semana:not(:is([data-rv], [data-rv-kids]) *) > .horario__linha:not(.is-in) { opacity: 0; }
  html.js:not(.quieto) :is(.faixa-ondas, .faixa-every):not(:is([data-rv], [data-rv-kids]) *, [data-rv], .is-in) { 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) :is(
    [data-rv], [data-rv-kids] > *, [data-rv-kids] > :only-child > *, [data-rv="lista"] > *, [data-rv="lista"] > :only-child > *,
    [data-rv] :is(img, video), [data-rv] .ln > span, [data-onda], :is(.h2, .titulo):not(h1) .ln > span,
    .horario__linha, .faixa-ondas, .faixa-every
  ) { animation: rv-rede var(--dur-fade-reduced) ease 2.6s both; }
}

@keyframes ln-sobe { from { translate: 0 calc(100% + .2em); } }
@keyframes chega { from { opacity: 0; translate: 0 8px; } }
@keyframes rv-rede { to { opacity: 1; translate: none; clip-path: none; -webkit-mask: none; mask: none; } }

/* 5. The ring of the seal turns as the seal crosses the screen: a third of a turn from entering at the
   bottom to leaving at the top. Scroll-driven, so it runs on the compositor and stops when the seal is
   off screen or the page is still. Without scroll timelines (Firefox today) it turns once in 28 s while
   site.js sees it on screen (html.selo-sem-scroll and .is-a-girar). */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    html:not(.quieto) [data-selo-gira] .selo-anel__anel {
      animation: selo-gira linear both;
      animation-timeline: view();
      animation-range: cover;
    }
  }
  html.selo-sem-scroll:not(.quieto) [data-selo-gira] .selo-anel__anel {
    animation: selo-volta var(--dur-seal-spin) linear infinite paused;
  }
  html.selo-sem-scroll:not(.quieto) [data-selo-gira].is-a-girar .selo-anel__anel { animation-play-state: running; }
}
@keyframes selo-gira { from { rotate: -60deg; } to { rotate: 60deg; } }
@keyframes selo-volta { to { rotate: 1turn; } }

/* 6. The phone menu's links follow the drawer down, one after another (the drawer itself: site.css). */
@media (prefers-reduced-motion: no-preference) {
  .menu[open] .menu__lista > li {
    transition: opacity var(--dur-enter) var(--ease-out), translate var(--dur-enter) var(--ease-out);
    transition-delay: calc(80ms + var(--menu-i, 0) * 40ms);
  }
  .menu__lista > li:nth-child(2) { --menu-i: 1; }
  .menu__lista > li:nth-child(3) { --menu-i: 2; }
  .menu__lista > li:nth-child(4) { --menu-i: 3; }
  .menu__lista > li:nth-child(n + 5) { --menu-i: 4; }
  @starting-style {
    .menu[open] .menu__lista > li { opacity: 0; translate: 0 12px; }
  }
}

/* 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] :is(img, video), [data-onda], .h2 .ln > span, .titulo .ln > span, .horario__linha, .faixa-ondas, .faixa-every) {
    opacity: 1 !important; translate: none !important; clip-path: none !important;
    -webkit-mask: none !important; mask: none !important; animation: none !important;
  }
}
