/* ──────────────────────────────────────────────────────────────────────────
   RASA — Page transition motion

   Loaded on every page, BEFORE the page's own stylesheet, so a page can
   always override. Three responsibilities:

     1. The root paint colour, so no navigation can ever flash white.
     2. The readiness gate's hidden state.
     3. The cross-document View Transition keyframes.

   Design intent (dark luxury): the transition should read as one surface
   being replaced by another under the SAME fixed chrome — not as two
   documents being swapped. The nav and footer therefore hold still while
   only the content moves. That single choice is most of what separates a
   premium app transition from a web page fade.
   ────────────────────────────────────────────────────────────────────────── */

/* ── 1. Never flash white ─────────────────────────────────────────────────
   MEASURED BUG (2026-08-21): <html> computed background-color was
   rgba(0,0,0,0) — fully transparent — with only <body> painted #080C14.
   Between documents the browser paints the ROOT colour, so on any load
   slower than the local dev server the user gets a white/system-coloured
   frame. This one line is the fix, and it matters most on the slow mobile
   connections the store reviewers will use.

   The colour is the brand background token, hardcoded here because this
   file must be able to paint before styles.css has parsed its variables. */
html {
  background-color: #080c14;
}

/* Overscroll (rubber-band past the top/bottom on trackpads and touch) also
   reveals the root colour. Same fix, stated explicitly. */
html,
body {
  background-color: #080c14;
}

/* ── 2. The readiness gate ────────────────────────────────────────────────
   visibility:hidden — NOT display:none. The page still lays out at its real
   dimensions, so when it appears there is zero layout shift; display:none
   would collapse the layout and cause exactly the footer-jump we are fixing.

   Scoped to <body> so the nav's fixed chrome and the page background are
   still painted — the user sees the brand surface, never a blank void. */
html[data-rasa-gate] body > *:not(nav):not(.nav):not(.cursor-glow):not(#topoCanvas) {
  visibility: hidden;
}

/* When the gate lifts, content appears with a short settle. Opacity+transform
   only — both composited, so this cannot cause layout work on reveal.

   SCOPED TO `:not(.rasa-vt)` ON PURPOSE. When cross-document View Transitions
   are running, js/page-transition.js already waits for readiness BEFORE it
   lets the cross-fade play, so the content is revealed by the transition
   itself. Running this settle as well would fade the same content in twice —
   a visible double-animation. This rule is therefore the fallback path:
   Safari/Firefox today, a direct page load, or a reload — anywhere no
   transition is playing. */
html:not(.rasa-vt)[data-rasa-ready] body > main,
html:not(.rasa-vt)[data-rasa-ready] body > footer,
html:not(.rasa-vt)[data-rasa-ready] body > .planner {
  animation: rasa-gate-in 0.42s cubic-bezier(0.16, 1, 0.3, 1) both;
}

@keyframes rasa-gate-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ── 3. Cross-document View Transitions ───────────────────────────────────
   Opt this document in. Both pages in a navigation must carry this rule for
   the browser to run a transition — which is why this file ships site-wide. */
@view-transition {
  navigation: auto;
}

/* The default UA transition cross-fades the ENTIRE page including the nav,
   which makes fixed chrome appear to blink. We stop that by giving the nav
   and footer their own transition names: an element with a stable name is
   matched across documents and animated independently — so identical chrome
   simply HOLDS STILL instead of fading out and back in.

   This is the single highest-impact rule in the file. */
.nav,
nav.nav {
  view-transition-name: rasa-nav;
}

.footer {
  view-transition-name: rasa-footer;
}

/* Chrome that is identical on both pages: no animation at all. Holding a
   perfectly still nav is what sells the illusion of one continuous app. */
::view-transition-old(rasa-nav),
::view-transition-new(rasa-nav),
::view-transition-old(rasa-footer),
::view-transition-new(rasa-footer) {
  animation: none;
  mix-blend-mode: normal;
}

/* Freeze the snapshot container too: disabling only its two images leaves
   the browser's automatic position/size interpolation active. Never stack
   two navs; retain the outgoing one only while the incoming auth slot is
   empty, then replace it with the live incoming nav in one paint. */
::view-transition-group(rasa-nav) { animation: none; }
::view-transition-old(rasa-nav) { opacity: 0; }
::view-transition-new(rasa-nav) { opacity: 1; }
html:has(.nav #navAuthWidget:empty)::view-transition-old(rasa-nav) { opacity: 1; }
html:has(.nav #navAuthWidget:empty)::view-transition-new(rasa-nav) { opacity: 0; }

/* The content area — everything that is NOT named above — is the `root`
   snapshot. This is what actually moves. */
::view-transition { pointer-events: none; }

::view-transition-group(root) {
  animation-duration: 0.28s;
  animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}

/* ── Timing: the two pages must never be half-visible at the same time ──
   MEASURED 2026-09-05 (planner → science, 96 frames): with the old page
   fading 1→0 and the new page 0→1 over the SAME 0.44s, the midpoint frame
   held both at ~50% — the planner's thread rail lay straight across the
   science headline as a pale line "surfacing" through it. A symmetric
   cross-fade is a double exposure by construction.

   Premium apps sequence instead of overlap: the outgoing surface is gone in
   0.08s (ease-IN, so it accelerates away), and the incoming surface starts
   0.06s in — by then the old one is under 25% and the new one is barely
   lit, so the overlap window carries almost no content from either. The
   ground the user sees between them is the brand background <html> paints
   (section 1), never white, never a void — the fixed nav holds still over it
   the entire time. */
::view-transition-old(root) {
  animation: rasa-out-fade 0.08s cubic-bezier(0.4, 0, 1, 1) both;
}
::view-transition-new(root) {
  animation: rasa-in-fade 0.22s cubic-bezier(0.16, 1, 0.3, 1) 0.06s both;
}
@keyframes rasa-out-fade {
  from { opacity: 1; }
  to   { opacity: 0; }
}
@keyframes rasa-in-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* `normal` instead of the default `plus-lighter`: plus-lighter ADDS the two
   snapshots together mid-transition, which on a dark surface momentarily
   brightens the page — reading as a flash. Normal blending keeps the
   background constant throughout. */
::view-transition-old(root),
::view-transition-new(root) {
  mix-blend-mode: normal;
  /* The old page must not paint over the new one as it leaves. */
  backface-visibility: hidden;
}

/* ── Directional motion ───────────────────────────────────────────────────
   data-vt-dir is stamped by js/page-transition.js from the nav order.
   Forward = the new page comes from the right (you moved deeper).
   Back     = the new page comes from the left (you returned).

   The distances are deliberately SMALL (8px). A big slide is a website
   effect; a small, fast, well-eased displacement is what native apps do and
   is what reads as expensive. */

html[data-vt-dir="forward"]::view-transition-old(root) {
  animation: rasa-out-left 0.08s cubic-bezier(0.4, 0, 1, 1) both;
}
html[data-vt-dir="forward"]::view-transition-new(root) {
  animation: rasa-in-right 0.22s cubic-bezier(0.16, 1, 0.3, 1) 0.06s both;
}

html[data-vt-dir="back"]::view-transition-old(root) {
  animation: rasa-out-right 0.08s cubic-bezier(0.4, 0, 1, 1) both;
}
html[data-vt-dir="back"]::view-transition-new(root) {
  animation: rasa-in-left 0.22s cubic-bezier(0.16, 1, 0.3, 1) 0.06s both;
}

/* Same sequencing as the fade above, with the 8px displacement on top. The
   previous version of this comment claimed the outgoing page "fades faster
   than the incoming one arrives" while both keyframes ran 0.44s — a claim,
   not a control. The durations above are now the control. */
@keyframes rasa-out-left {
  from {
    opacity: 1;
    transform: none;
  }
  to {
    opacity: 0;
    transform: translateX(-8px);
  }
}

@keyframes rasa-in-right {
  from {
    opacity: 0;
    transform: translateX(8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes rasa-out-right {
  from {
    opacity: 1;
    transform: none;
  }
  to {
    opacity: 0;
    transform: translateX(8px);
  }
}

@keyframes rasa-in-left {
  from {
    opacity: 0;
    transform: translateX(-8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ── Accessibility ────────────────────────────────────────────────────────
   A user who has asked their operating system for less motion gets a clean
   instant swap. We never keep "just a little" movement — the setting is a
   request, not a suggestion. The readiness gate still applies, so they still
   get the correct-first-time page, just without the slide. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }

  html[data-rasa-ready] body > main,
  html[data-rasa-ready] body > footer,
  html[data-rasa-ready] body > .planner {
    animation: none !important;
  }
}
