/* page-fade.css — the site's cross-document page transition.
   ==========================================================================
   ONE behaviour, stated plainly: going to a NEW PAGE fades out and in. Going to
   a part of the page you are already on does not.

   The second half needs no code. A same-document fragment link (`#pricing-faq`)
   is not a navigation, so no view transition is ever started for it; the browser
   just scrolls. That is the whole reason this is built on cross-document view
   transitions rather than on a JS router: the distinction the user asked for is
   the distinction the platform already draws.

   WHY THIS IS ITS OWN FILE, AND OPT-IN PER PAGE
   ---------------------------------------------
   `@view-transition` is a document-level at-rule. It cannot be scoped by a
   selector, so putting it in backbone.css (42 pages link that) would opt in
   every page on the site including the ones that must not have it.

   A cross-document transition begins at the INCOMING document's first render.
   For a page whose body is server-rendered HTML that first render is the page,
   and the fade is exactly right. For a page whose body is built by a deferred
   module the first render is an EMPTY frame, so the browser faithfully
   cross-fades the page you were reading into nothing and then pops the content
   in — which is worse than no transition at all. The admin console shipped that
   bug and needed a whole handover mechanism (`data-shell-ready`, and the block
   at the end of admin.css) to get out of it.

   So a page opts in by linking this file, and it may only do that if its main
   content is in its own HTML. The realms that render from JS either carry their
   own tuned transition (`/admin/`, `/my/`) or deliberately have none.

   BOTH DOCUMENTS MUST OPT IN for a cross-document transition to run. Leaving a
   page that links this for one that does not is simply an ordinary navigation,
   which is the correct outcome: the fade is a promise that the next thing is
   ready, and it should not be made on behalf of a page that cannot keep it.
   ========================================================================== */

@view-transition {
  navigation: auto;
}

/* The fade itself.
   Leaving is faster than arriving (140ms out, 220ms in) — the house rule from
   `design-reference`, and the reason a transition reads as the new page coming
   toward you rather than as the old one being dragged away.

   OPACITY ONLY. No translate, no blur, no scale. This runs on every link on the
   public site, several of which are long documents of small text; anything with
   a transform turns a paragraph into a smear twice per navigation, and anything
   with a blur costs a full-page filter on hardware that does not always have it
   to spare. The one job here is to remove the white flash between two pages.

   `mix-blend-mode: normal` on both, because the UA stacks the pair with
   `plus-lighter` for its own cross-fade and two opaque page images blended that
   way wash out to white in the middle of the transition. */
/* ONE THING FADES, NOT TWO. THIS IS THE WHOLE FIX, AND IT IS NOT A STYLE CHOICE.
   ---------------------------------------------------------------------------
   The previous shape animated BOTH snapshots from t=0: old 1→0 over 140ms, new 0→1 over 220ms.
   Those curves do not sum to 1. At 140ms the old is gone and the new is only 64% of the way in, so
   36% of the screen is neither page — and what is behind them is the UA canvas. Measured on a
   /browse/ → /home/ navigation with a CDP screencast: the midpoint frame shows both pages ghosted
   through each other and the whole image lifted toward white. That is the "big white flash", and it
   happens on every public navigation, not just this one.

   `plus-lighter` is the blend mode designed to make a two-sided cross-fade sum correctly, and the UA
   default uses it. This file cannot: two OPAQUE page images blended that way wash out to white in
   the middle, which is the bug the `mix-blend-mode: normal` lines below were added to fix. Fixing the
   blend without fixing the curves traded a wash-out for a trough.

   So only the incoming page animates. The outgoing one is HELD at full opacity underneath it — an
   `::view-transition-old` paints below its `::view-transition-new` in the pair — and the new fades in
   on top of it. Total coverage is 100% opaque at every instant of the transition, so nothing shows
   through, neither page is ever translucent, and there is no canvas to see. It still reads as a
   cross-fade, because compositing a fading-in image over a held one is exactly what a cross-fade
   looks like.

   The house rule that leaving is faster than arriving is not violated; it no longer applies. There is
   no leaving animation to be faster — the old page is simply covered. 220ms, the arriving duration,
   is unchanged. */
::view-transition-old(root) {
  animation: none;
  opacity: 1;
  mix-blend-mode: normal;
}
::view-transition-new(root) {
  animation: bb-page-in 220ms cubic-bezier(0.16, 1, 0.3, 1) both;
  mix-blend-mode: normal;
}
@keyframes bb-page-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Insurance, and cheap: a floor colour behind the pair for the one case the rule above cannot cover
   — two pages of different heights, where the held old snapshot does not reach the full snapshot
   containing block and a strip of canvas is exposed at the bottom.
   Scoped to the transition rather than solved by putting a background on `html`, which would be the
   other way to do it and has a side effect nobody asked for: `html` is transparent on every page in
   this product except the chat, so `body`'s background is what propagates to the canvas today.
   Giving `html` one stops that propagation, and `body`'s `--bb-backdrop` texture (sized `100% 100%`
   to BODY's box) would then end at a visible edge on any page shorter than the viewport. */
::view-transition-image-pair(root) {
  background-color: var(--deep-1);
}

/* ── Ghosts arriving from an app realm ─────────────────────────────────────
   THE RULE THAT MAKES THIS NECESSARY: the pseudo-element tree of a
   cross-document transition belongs to the INCOMING document. Only the page
   being navigated TO can style a snapshot arriving from the page being
   navigated FROM.

   Three stylesheets in this product assign `view-transition-name`s to their own
   chrome so it holds still while only the pane changes:
     my.css        → my-topbar
     admin.css     → admin-sidebar, admin-topbar, admin-tabs, gcb-route-pane
     platform.css  → gcb-route-pane
   `/my/` ↔ `/admin/` is clean because each of those two stylesheets suppresses
   the other's names when it is the incoming document. Nothing did that for a
   PUBLIC page, because until this file existed a public page never opted in and
   the crossing was an ordinary navigation.

   Now that it does opt in, leaving `/my/` or the console for a public page
   produces an old-only snapshot for each of those names with no new element to
   pair with — and an unstyled old-only group gets the UA's default fade-out. In
   practice: the console's rail and topbar, or the dashboard's nav bar, hanging
   over the new page after it has already painted. It fires on every footer link
   `chrome.js` renders into those realms, not just on scripted redirects.

   `display: none` rather than `animation: none`, and both halves matter: it
   hides the ghost AND terminates the UA animation, so it also stops the
   snapshot holding the transition open past the root fade. This is the same
   shape my.css and admin.css already use on each other.

   These names are foreign to every page that links this file, so each of these
   rules is inert except on exactly the crossing it exists for. */
::view-transition-old(my-topbar),
::view-transition-old(admin-sidebar),
::view-transition-old(admin-topbar),
::view-transition-old(admin-tabs),
::view-transition-old(gcb-route-pane) {
  display: none;
}

/* Reduced motion: a CUT, not a slower fade.
   Both signals — the OS preference and the in-app `data-a11y-motion` switch —
   and `animation: none` is the right reduction HERE, unlike in the admin console
   where it would hand the navigation back to the browser default and put the
   blank frame back. There is no blank frame to defend against on a page whose
   content is in its own HTML: with no animation the transition ends immediately
   and the new page is simply there.

   backbone.css's blanket `*, *::before, *::after { animation-duration: .001ms }`
   does NOT reach these — a view-transition pseudo-element is not a descendant of
   any matched element — so they are named explicitly.

   Still correct under the held-old shape above: `animation: none` on the NEW
   snapshot leaves it at its natural opacity of 1 from the first frame, over an
   old that is already opaque, so the cut is clean rather than a blank. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation: none;
  }
}
:root[data-a11y-motion="reduced"]::view-transition-old(root),
:root[data-a11y-motion="reduced"]::view-transition-new(root) {
  animation: none;
}
