/* ---------------------------------------------------------------
   Showcase scenes — Etho's app UI, acted out inside the phone

   A scene sits over its chapter's film and plays a short, simplified
   interaction on a clock: the app rises, a cursor taps, the screen
   answers. scenes.js only adds and removes state classes (s-*) on the
   scene and moves the cursor; every move is a CSS transition below,
   on the shared --ease-out curve, so the whole scene keeps one feel.

   The look is the app's own (etho: src/components/theme.ts and
   explore/tokens.ts): the grey-to-white page, photo cards, the black
   offer pill, black titles over grey lines, sheets over a frosted
   page. It is a simplified version of the app, never a copy of a real
   screen: fewer pieces, bigger type, one idea per screen.

   Sizes are in em off one font size tied to the phone's width (a 375
   pt phone is 23.4em wide), so the UI scales with the frame. Only
   leaf text sets its own font-size, so ems never compound. With no
   script everything waits below the screen and the film carries the
   chapter.
   --------------------------------------------------------------- */

.scene {
  /* The app's light theme. */
  --sx-top: #E6E5E3;
  --sx-mid: #FBFBFB;
  --sx-bottom: #FCFCFC;
  --sx-card: #FFFFFF;
  --sx-title: #000000;
  --sx-text2: rgba(14, 15, 12, 0.59);
  --sx-was: #717171;
  --sx-green: #177245;
  --sx-rule: #E4E6E8;
  --sx-edge: rgba(0, 0, 0, 0.25);
  --sx-shadow: 0 0 0 1px rgba(14, 15, 12, 0.04), 0 1px 2px rgba(14, 15, 12, 0.04), 0 6px 16px rgba(14, 15, 12, 0.06);

  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  font-family: var(--body-font);
  font-size: calc(var(--phone-h) * 0.5 / 23.4);
  line-height: 1.3;
  /* The stacked phone layout centres its text; the app does not. */
  text-align: left;
  color: var(--sx-title);
  pointer-events: none;
  user-select: none;
  -webkit-font-smoothing: antialiased;
}
.scene *,
.scene *::before,
.scene *::after { box-sizing: border-box; }
.scene p { margin: 0; }
.scene img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* The grooming room is a tall shot with the dog in the tub left of
   centre and low; every crop of it holds on the dog. */
.scene img.sx-groom { object-position: 29% 58%; }
/* The training floor is a wide shot; its crops hold on the dogs. */
.scene img.sx-focus-training { object-position: 42% 70%; }
/* In the small crops the room would leave the dog a speck, so those
   step in on the tub. */
.sx-card img.sx-groom,
.sx-booking img.sx-groom { transform: scale(1.75); transform-origin: 30% 57%; }

/* Snapping back to the start between loops, and the whole scene under
   reduced motion: no transitions at all. */
.scene.is-resetting,
.scene.is-resetting * { transition: none !important; }

/* -- Hector's picks, rising over the film ---------------------------

   A simplified home, not the app's own: no tabs, no search, one big
   line and one row of large cards to fling through. Made from the
   app's pieces (the page, the photo cards, the black offer pill) so it
   reads as Etho without showing the real screen. */

.scene__app {
  position: absolute;
  inset: 0;
  padding: 0 0 0 1.5em;
  background: linear-gradient(180deg, var(--sx-top) 0%, var(--sx-mid) 23.5%, var(--sx-bottom) 100%);
  border-radius: calc(var(--phone-h) * 0.063) calc(var(--phone-h) * 0.063) 0 0;
  transform: translateY(102%);
  transition: transform 850ms var(--ease-out), filter 600ms var(--ease-out);
}
.s-app .scene__app { transform: none; }
/* Behind a sheet the page frosts and dims, as under the app's sheets. */
.s-sheet .scene__app { filter: blur(4px) brightness(0.88); }


/* The app's own header: the greeting on the left, Hector's photo on
   the right. */
.sx-hello {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 3em;
  padding-right: 1.5em;
}
.sx-greeting {
  font-size: 1.1em;
  font-weight: 700;
  color: #464646;
}
.sx-avatar {
  position: relative;
  flex: none;
  width: 2.5em;
  height: 2.5em;
}
.sx-avatar img { border-radius: 50%; }

.sx-headline {
  margin-top: 1.7em !important;
  font-size: 1.6em;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.02em;
}
.sx-heading {
  margin-top: 2.1em !important;
  font-size: 1.05em;
  font-weight: 700;
  color: #464646;
}

/* One row of large photo cards, the next peeking past the edge. Each
   comes up a beat after the one before. */
.sx-rail {
  display: flex;
  gap: 0.85em;
  margin-top: 1em;
}
.sx-rail--small { gap: 0.8em; margin-top: 1.15em; }
/* The row the cursor scrolls: scenes.js drags and flings it by its
   transform, so it never reflows. */
.sx-rail[data-scroll] { will-change: transform; }
.sx-card {
  flex: none;
  width: 10.2em;
  opacity: 0;
  transform: translateY(1.6em);
  transition: opacity 600ms var(--ease-out), transform 750ms var(--ease-out);
}
.s-app .sx-card { opacity: 1; transform: none; transition-delay: 380ms; }
.s-app .sx-card:nth-child(1) { transition-delay: 250ms; }
.sx-rail--small .sx-card { width: 7.4em; }
.s-app .sx-rail--small .sx-card { transition-delay: 480ms; }

.sx-photo {
  position: relative;
  height: 11.6em;
  border-radius: 1.2em;
  overflow: hidden;
  background: var(--sx-top);
  box-shadow: var(--sx-shadow);
  transition: transform 450ms var(--ease-out);
}
.sx-photo .sx-pill {
  position: absolute;
  top: 0.7em;
  right: 0.7em;
}
.sx-rail--small .sx-photo { height: 7.6em; border-radius: 1em; }
.sx-rail--small .sx-photo .sx-pill { top: 0.55em; right: 0.55em; }
/* While the row is in flight its cards draw in a little, and swell
   back as it comes to rest: the fun part of the fling. */
.s-fling .sx-photo { transform: scale(0.92); }
/* Once it settles, the picked card's offer gives a small hop. */
.s-pick [data-target="wash"] .sx-pill,
.s-pick .sx-result:first-child .sx-pill,
.s-pick .sx-svc:first-child .sx-pill { animation: sx-hop 600ms var(--ease-out); }
@keyframes sx-hop {
  35% { transform: translateY(-0.35em) scale(1.12); }
  100% { transform: none; }
}

/* The offer pill: the app's one solid black shape, so the saving is
   the first thing the eye finds. */
.sx-pill {
  display: inline-block;
  padding: 0.36em 0.72em;
  border-radius: 2em;
  background: #000000;
  color: #FFFFFF;
  font-size: 0.86em;
  font-weight: 700;
  line-height: 1.2;
  white-space: nowrap;
}

.sx-title {
  margin-top: 0.65em !important;
  padding-left: 0.2em;
  font-size: 0.9em;
  font-weight: 700;
}
.sx-line {
  margin-top: 0.15em !important;
  font-size: 0.82em;
  font-weight: 500;
  color: var(--sx-text2);
}
.sx-line--price {
  margin-top: 0.1em !important;
  padding-left: 0.25em;
  font-size: 0.85em;
  font-weight: 700;
  color: var(--sx-title);
}
.sx-line s,
.sx-price s {
  margin-right: 0.1em;
  font-weight: 500;
  color: var(--sx-was);
}

/* -- The sheet ------------------------------------------------------ */

.scene__sheet {
  position: absolute;
  left: 0.55em;
  right: 0.55em;
  bottom: 0.55em;
  /* Tall enough for the extras page, the tallest; the frosted page
     shows above it. */
  height: 25.6em;
  border-radius: 1.5em;
  background: var(--sx-card);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
  overflow: hidden;
  transform: translateY(110%);
  transition: transform 750ms var(--ease-out);
}
.s-sheet .scene__sheet { transform: none; }

.scene__page {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  /* Text sits 1.15em in; the photo and the button reach out to 0.6em. */
  padding: 0.6em 1.15em 0.7em;
  transition: transform 650ms var(--ease-out), opacity 500ms var(--ease-out);
}
/* Each page pushes in from the right, iOS-style, as the one before
   slides a short way left and fades under it. */
.scene__page--extras,
.scene__page--done { transform: translateX(100%); }
.s-extras .scene__page--service { transform: translateX(-30%); opacity: 0; }
.s-extras .scene__page--extras { transform: none; }
.s-done .scene__page--extras { transform: translateX(-30%); opacity: 0; }
.s-done .scene__page--done { transform: none; }

.sx-hero {
  position: relative;
  flex: none;
  margin: 0 -0.55em;
  height: 11.5em;
  border-radius: 1.1em;
  overflow: hidden;
}
.sx-hero .sx-pill { position: absolute; top: 0.75em; right: 0.75em; font-size: 0.95em; }

.sx-meta {
  display: flex;
  align-items: center;
  gap: 0.3em;
  margin-top: 0.95em !important;
  font-size: 0.8em;
  font-weight: 500;
  color: var(--sx-text2);
}
.sx-meta span { font-weight: 700; color: var(--sx-title); margin-right: 0.35em; }
.sx-meta svg { width: 0.95em; height: 0.95em; fill: var(--sx-title); }

.sx-big {
  margin-top: 0.25em !important;
  font-size: 1.45em;
  font-weight: 700;
  letter-spacing: -0.01em;
}
.sx-price {
  margin-top: 0.4em !important;
  font-size: 1.15em;
  font-weight: 700;
}

.sx-button {
  margin: auto calc(-0.55em / 0.95) 0;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 2.9em;
  border-radius: 1.45em;
  background: #000000;
  color: #FFFFFF;
  font-size: 0.95em;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  transition: transform 160ms var(--ease-out);
}

/* Make it special: a photo, a name, a price and a round check, each
   row ruled off from the next, as the storefront's add-ons are. */
.scene__page--extras { padding-top: 1.2em; }
.scene__page--extras > .sx-big { margin-top: 0 !important; }
.sx-rows { margin-top: 0.7em; }
.sx-row {
  display: flex;
  align-items: center;
  gap: 0.8em;
  padding: 0.6em 0;
  border-bottom: 1px solid var(--sx-rule);
}
.sx-row img {
  flex: none;
  width: 3.3em;
  height: 3.3em;
  border-radius: 0.8em;
}
.sx-row > div { flex: 1; min-width: 0; }
.sx-row .sx-title { margin: 0 !important; padding: 0; font-size: 0.92em; }
.sx-row .sx-line .sx-pill { font-size: 1.05em; }

.sx-check {
  flex: none;
  display: grid;
  place-items: center;
  width: 1.55em;
  height: 1.55em;
  border-radius: 50%;
  border: 1.5px solid var(--sx-edge);
  transition: background 300ms var(--ease-out), border-color 300ms var(--ease-out), transform 300ms var(--ease-out);
}
.sx-check svg {
  width: 1em;
  height: 1em;
  fill: none;
  stroke: #FFFFFF;
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.sx-check path {
  stroke-dasharray: 20;
  stroke-dashoffset: 20;
  transition: stroke-dashoffset 420ms var(--ease-out) 120ms;
}
.s-photo [data-target="photo"] .sx-check,
.s-dental [data-target="dental"] .sx-check { background: #000000; border-color: #000000; }
.s-photo [data-target="photo"] .sx-check path,
.s-dental [data-target="dental"] .sx-check path { stroke-dashoffset: 0; }

/* The saving, in the app's green, above the button. */
.sx-sum {
  display: flex;
  justify-content: space-between;
  margin: auto 0 0.75em !important;
  font-size: 0.88em;
  font-weight: 600;
}
.sx-sum b { color: var(--sx-green); font-weight: 700; }
.sx-sum + .sx-button { margin-top: 0; }

/* Booked: a tick that draws itself, the booking as a card, and the
   saving. */
.scene__page--done {
  align-items: center;
  justify-content: center;
  text-align: center;
  padding-bottom: 1.6em;
}
.sx-done {
  display: grid;
  place-items: center;
  width: 4.4em;
  height: 4.4em;
  border-radius: 50%;
  background: #000000;
  transform: scale(0.6);
  opacity: 0;
  transition: transform 700ms var(--ease-out) 150ms, opacity 300ms var(--ease-out) 150ms;
}
.sx-done svg {
  width: 2.5em;
  height: 2.5em;
  fill: none;
  stroke: #FFFFFF;
  stroke-width: 4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.sx-done path {
  stroke-dasharray: 36;
  stroke-dashoffset: 36;
  transition: stroke-dashoffset 600ms var(--ease-out) 550ms;
}
.s-done .sx-done { transform: none; opacity: 1; }
.s-done .sx-done path { stroke-dashoffset: 0; }
.scene__page--done > .sx-big { margin-top: 0.5em !important; }

.sx-booking {
  display: flex;
  align-items: center;
  gap: 0.75em;
  align-self: stretch;
  margin-inline: -0.55em;
  margin-top: 1.2em;
  padding: 0.55em;
  border-radius: 1.2em;
  background: var(--sx-card);
  box-shadow: var(--sx-shadow);
  text-align: left;
  opacity: 0;
  transform: translateY(0.8em);
  transition: opacity 400ms var(--ease-out) 750ms, transform 600ms var(--ease-out) 750ms;
}
.sx-thumb { flex: none; width: 3.2em; height: 3.2em; border-radius: 0.8em; overflow: hidden; }
.sx-booking .sx-title { margin: 0 !important; padding: 0; font-size: 0.92em; }
.s-done .sx-booking { opacity: 1; transform: none; }

.sx-saved {
  margin-top: 1.1em;
  padding: 0.5em 1.1em;
  border-radius: 2em;
  background: var(--sx-green);
  color: #FFFFFF;
  font-size: 1.2em;
  font-weight: 700;
  opacity: 0;
  transform: translateY(0.8em);
  transition: opacity 400ms var(--ease-out) 1000ms, transform 600ms var(--ease-out) 1000ms;
}
.s-done .sx-saved { opacity: 1; transform: none; }

/* Going out at the end of a loop, the sheet and the page drop away
   together and the film has the screen again. */
.s-out .scene__sheet,
.s-out .scene__app { transform: translateY(105%); filter: none; transition-duration: 650ms; }

/* Whatever the cursor taps gives a little under it: a card or button
   as a whole, a row only at its check. The card keeps its entrance
   timing and only shortens it while pressed. */
.sx-card.is-pressed { transform: scale(0.95); transition-duration: 160ms; transition-delay: 0ms; }
.sx-button.is-pressed { transform: scale(0.96); }
.sx-row.is-pressed .sx-check { transform: scale(0.82); }

/* Count-up: a small lift as the total changes. */
[data-scene-count] { display: inline-block; }
[data-scene-count].is-bump { animation: sx-bump 520ms var(--ease-out); }
@keyframes sx-bump {
  30% { transform: scale(1.15); }
  100% { transform: none; }
}

/* -- Search ------------------------------------------------------- */

/* The search bar, typed into by scenes.js (type). Until a letter is in
   it shows the app's placeholder; while s-typing is on, a caret blinks
   after the text. */
.sx-search {
  display: flex;
  align-items: center;
  gap: 0.55em;
  height: 2.75em;
  margin: 1em 1.5em 0 0;
  padding: 0 1.1em;
  border-radius: 2em;
  background: #FFFFFF;
  box-shadow: 0 0.14em 0.7em rgba(0, 0, 0, 0.14);
}
.sx-search svg {
  flex: none;
  width: 1.05em;
  height: 1.05em;
  fill: none;
  stroke: var(--sx-text2);
  stroke-width: 2;
  stroke-linecap: round;
}
.sx-type {
  font-size: 0.9em;
  font-weight: 600;
  white-space: nowrap;
}
.sx-type:empty::before {
  content: 'Search services, events, or needs...';
  font-weight: 500;
  color: var(--sx-text2);
}
.s-typing .sx-type::after {
  content: '';
  display: inline-block;
  width: 0.09em;
  height: 1.15em;
  margin-left: 0.08em;
  vertical-align: -0.2em;
  background: var(--sx-title);
  animation: sx-caret 900ms steps(1) infinite;
}
.s-typing .sx-type:empty::before { content: none; }
@keyframes sx-caret { 50% { opacity: 0; } }

/* -- Chapter 2, first screen: a search on frosted glass -------------

   No page and no sharp photo: the slide's still is the room already
   frosted, and the search sits large in the middle of it from the
   first frame. Once the words are in, the bar rises (s-up), three
   answers come in as glass (s-results), and a success screen slides
   up over everything (s-success). */

.scene__app--clear,
.s-app .scene__app--clear {
  background: none;
  transform: none;
  transition: opacity 600ms var(--ease-out);
}

.sx-search--hero {
  position: absolute;
  left: 1.2em;
  right: 1.2em;
  top: 50%;
  height: 3.5em;
  margin: -1.75em 0 0;
  padding: 0 1.3em;
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: blur(16px) saturate(1.4);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  box-shadow:
    inset 0.05em 0.05em 0 rgba(255, 255, 255, 0.8),
    0 0.5em 1.8em rgba(14, 15, 12, 0.14);
  transition: top 800ms var(--ease-out), margin 800ms var(--ease-out), height 800ms var(--ease-out);
}
.sx-search--hero svg { width: 1.3em; height: 1.3em; }
.sx-search--hero .sx-type { font-size: 1.1em; }
/* The placeholder is long; it sits a step under the typed words. */
.sx-search--hero .sx-type:empty::before { font-size: 0.84em; }
.s-up .sx-search--hero { top: 2.4em; margin-top: 0; height: 3em; }

.sx-results {
  position: absolute;
  top: 6.4em;
  left: 1.2em;
  right: 1.2em;
}
.sx-result {
  display: flex;
  align-items: center;
  gap: 0.85em;
  margin-top: 0.7em;
  padding: 0.55em;
  border-radius: 1.45em;
  background: rgba(255, 255, 255, 0.7);
  backdrop-filter: blur(18px) saturate(1.4);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  box-shadow:
    inset 0.05em 0.05em 0 rgba(255, 255, 255, 0.7),
    0 0.4em 1.4em rgba(14, 15, 12, 0.1);
  opacity: 0;
  transform: translateY(1.4em);
  transition: opacity 600ms var(--ease-out), transform 750ms var(--ease-out);
}
.s-results .sx-result { opacity: 1; transform: none; }
/* Tapped: the card gives, quickly, whatever its entrance timing. */
.s-results .sx-result.is-pressed { transform: scale(0.97); transition-duration: 160ms; transition-delay: 0ms; }
.s-results .sx-result:nth-child(2) { transition-delay: 110ms; }
.s-results .sx-result:nth-child(3) { transition-delay: 220ms; }
.sx-result__photo {
  position: relative;
  flex: none;
  width: 6em;
  height: 6em;
  border-radius: 1em;
  overflow: hidden;
}
.sx-result__photo .sx-pill { position: absolute; top: 0.45em; left: 0.45em; }
.sx-result .sx-title { margin: 0 !important; padding: 0; font-size: 0.95em; }
.sx-result .sx-line { margin-top: 0.25em !important; line-height: 1.35; }

/* The success screen, after Cash App's: a white sheet with a close
   button and a title bar, a big line, the picture in a rounded frame,
   and the saving in a panel along the foot. Its parts land in turn. */
.sx-success {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  padding: 0 1.3em;
  background: #FFFFFF;
  border-radius: calc(var(--phone-h) * 0.063) calc(var(--phone-h) * 0.063) 0 0;
  transform: translateY(103%);
  transition: transform 850ms var(--ease-out);
}
.s-success .sx-success { transform: none; }
.sx-nav {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 2.8em;
  margin-top: 1.5em;
}
.sx-nav p { font-size: 0.85em; font-weight: 600; }
.sx-close {
  position: absolute;
  left: -0.2em;
  display: grid;
  place-items: center;
  width: 2.5em;
  height: 2.5em;
  border-radius: 50%;
  background: #FFFFFF;
  box-shadow: 0 0.1em 0.5em rgba(0, 0, 0, 0.12);
}
.sx-close svg {
  width: 1.1em;
  height: 1.1em;
  fill: none;
  stroke: var(--sx-title);
  stroke-width: 2.4;
  stroke-linecap: round;
}
.sx-cheer {
  margin-top: 1.1em !important;
  font-size: 1.75em;
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.025em;
  opacity: 0;
  transform: translateY(0.6em);
  transition: opacity 500ms var(--ease-out) 300ms, transform 700ms var(--ease-out) 300ms;
}
.sx-frame {
  align-self: center;
  width: 13em;
  height: 17em;
  margin: auto 0;
  border-radius: 1.9em;
  overflow: hidden;
  opacity: 0;
  transform: scale(0.85) rotate(-4deg);
  transition: opacity 500ms var(--ease-out) 450ms, transform 900ms var(--ease-out) 450ms;
}
.sx-benefit {
  margin: 0 -0.75em 0.55em;
  padding: 1.25em 1.3em 1.6em;
  border-radius: 1.6em;
  background: #F2F1EF;
  opacity: 0;
  transform: translateY(2em);
  transition: opacity 500ms var(--ease-out) 650ms, transform 800ms var(--ease-out) 650ms;
}
.sx-benefit p { font-size: 1.3em; font-weight: 600; letter-spacing: -0.02em; }
.sx-benefit b { font-weight: 700; color: var(--sx-green); }
.s-success .sx-cheer,
.s-success .sx-benefit { opacity: 1; transform: none; }
.s-success .sx-frame { opacity: 1; transform: none; }

.scene--glass.s-out .scene__app--clear { opacity: 0; }

/* -- Chapter 2, second screen: Winnie ------------------------------

   Opens like the puppy screen: a large search on her photo, frosted.
   Once "Winnie has hip pain" is in, the page fades in behind (s-app),
   the bar settles under the header (s-up), and three large therapies
   fill the screen (s-list). Tapping hydrotherapy brings up a
   confirmation built like the puppy's success screen (s-success),
   with "The little extras matter"; the ramp is picked (s-ramp) and
   Shop tapped (s-shop). */

/* Frosts the film once the search is in: a blur of whatever plays
   behind, under a pale veil. */
.sx-veil {
  position: absolute;
  inset: 0;
  background: rgba(250, 249, 246, 0.38);
  opacity: 0;
  transition: opacity 800ms var(--ease-out);
}
.s-frost .sx-veil { opacity: 1; }
/* The blur itself is on the film (and its still): a backdrop blur
   cannot reach a playing video in every browser. Scaled a touch so
   the soft edges stay outside the screen. */
.showcase__slide:has(.scene--winnie) .showcase__film,
.showcase__slide:has(.scene--winnie) .showcase__still {
  transition: filter 800ms var(--ease-out), transform 800ms var(--ease-out), opacity 200ms var(--ease);
}
.showcase__slide:has(.scene--winnie.s-frost) .showcase__film,
.showcase__slide:has(.scene--winnie.s-frost) .showcase__still {
  filter: blur(14px) saturate(1.1);
  transform: scale(1.08);
}
.showcase__slide:has(.scene--winnie.is-resetting) .showcase__film,
.showcase__slide:has(.scene--winnie.is-resetting) .showcase__still { transition: none; }
/* Winnie's search waits for the film, then rises in over it. */
.scene--winnie .sx-search--hero {
  opacity: 0;
  transform: translateY(1em) scale(0.96);
  transition:
    opacity 600ms var(--ease-out), transform 800ms var(--ease-out),
    top 800ms var(--ease-out), margin 800ms var(--ease-out), height 800ms var(--ease-out);
}
.scene--winnie.s-bar .sx-search--hero { opacity: 1; transform: none; }
.scene--winnie .sx-type:empty::before { content: 'Search for anything'; }

.sx-page {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, var(--sx-top) 0%, var(--sx-mid) 23.5%, var(--sx-bottom) 100%);
  opacity: 0;
  transition: opacity 700ms var(--ease-out);
}
.s-app .sx-page { opacity: 1; }
.scene--winnie .sx-hello {
  opacity: 0;
  transform: translateY(-0.6em);
  transition: opacity 500ms var(--ease-out) 150ms, transform 700ms var(--ease-out) 150ms;
}
.scene--winnie.s-app .sx-hello { opacity: 1; transform: none; }
.scene--winnie.s-up .sx-search--hero { top: 6.3em; margin-top: 0; height: 3em; background: #FFFFFF; }

.sx-subhead {
  font-size: 0.95em;
  font-weight: 700;
  color: #464646;
}

.sx-tcards {
  position: absolute;
  top: 10.3em;
  left: 1.2em;
  right: 1.2em;
}
.sx-tcards .sx-subhead { opacity: 0; transition: opacity 500ms var(--ease-out); }
.s-list .sx-tcards .sx-subhead { opacity: 1; }
.sx-tcard {
  display: flex;
  align-items: center;
  gap: 1em;
  margin-top: 0.75em;
  padding: 0.6em;
  border-radius: 1.6em;
  background: #FFFFFF;
  box-shadow: var(--sx-shadow);
  opacity: 0;
  transform: translateY(1.6em);
  transition: opacity 600ms var(--ease-out), transform 750ms var(--ease-out);
}
.s-list .sx-tcard { opacity: 1; transform: none; }
.s-list .sx-tcard:nth-of-type(2) { transition-delay: 100ms; }
.s-list .sx-tcard:nth-of-type(3) { transition-delay: 200ms; }
.s-list .sx-tcard.is-pressed { transform: scale(0.97); transition-duration: 160ms; transition-delay: 0ms; }
.sx-tcard__photo {
  position: relative;
  flex: none;
  width: 9.2em;
  height: 9.2em;
  border-radius: 1.15em;
  overflow: hidden;
}
.sx-tcard__photo .sx-pill { position: absolute; top: 0.55em; left: 0.55em; }
.sx-tcard .sx-title { margin: 0 !important; padding: 0; font-size: 1.15em; }
.sx-tcard .sx-line { margin-top: 0.35em !important; font-size: 0.9em; line-height: 1.4; }
.scene--winnie.s-pick .sx-tcard:first-of-type .sx-pill,
.scene--trip.s-pick [data-target="hotel"] .sx-pill { animation: sx-hop 600ms var(--ease-out); }
/* Each therapy's photo holds on its dog. */
.scene img.sx-focus-hydro { object-position: 42% 40%; }
.scene img.sx-focus-redlight { object-position: 88% 60%; }
.scene img.sx-focus-pilates { object-position: 74% 50%; }
.scene img.sx-focus-feeder { object-position: 45% 70%; }

/* The confirmation: the puppy's success screen, with the extras in
   place of its picture and Shop in its foot panel. */
.sx-success--extras .sx-cheer { font-size: 1.6em; }
.sx-extras {
  margin: auto 0;
  opacity: 0;
  transform: translateY(1.4em);
  transition: opacity 500ms var(--ease-out) 550ms, transform 800ms var(--ease-out) 550ms;
}
.s-success .sx-extras { opacity: 1; transform: none; }
.sx-extras__row { display: flex; gap: 0.85em; margin-top: 0.8em; }
.sx-extra { flex: 1; min-width: 0; transition: transform 160ms var(--ease-out); }
.sx-extra.is-pressed { transform: scale(0.96); }
.sx-extra__photo {
  position: relative;
  height: 14em;
  border-radius: 1.4em;
  overflow: hidden;
  background: #EFEDE8;
  transition: box-shadow 300ms var(--ease-out);
}
.sx-extra__photo .sx-check {
  position: absolute;
  top: 0.6em;
  right: 0.6em;
  background: rgba(255, 255, 255, 0.85);
  border-color: rgba(255, 255, 255, 0.95);
}
.s-ramp [data-target="ramp"] .sx-extra__photo { box-shadow: inset 0 0 0 0.16em #000000; }
.s-ramp [data-target="ramp"] .sx-check { background: #000000; border-color: #000000; }
.s-ramp [data-target="ramp"] .sx-check path { stroke-dashoffset: 0; }
.sx-extra .sx-title { margin-top: 0.6em !important; padding: 0; font-size: 0.95em; }
.sx-extra .sx-line--price { padding: 0; }

.sx-benefit--shop { padding: 0.9em; }
/* The total opens above Shop once the ramp is added: Etho's savings on
   the booking and the ramp, counted up in the app's green. */
.sx-total {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 600ms var(--ease-out);
}
.sx-total > p {
  overflow: hidden;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 0 0.3em;
  font-size: 1.15em;
  font-weight: 600;
  letter-spacing: -0.02em;
  opacity: 0;
  transition: opacity 400ms var(--ease-out) 200ms, padding 600ms var(--ease-out);
}
.sx-total b { font-size: 1.35em; font-weight: 700; color: var(--sx-green); font-variant-numeric: tabular-nums; }
.s-total .sx-total { grid-template-rows: 1fr; }
.s-total .sx-total > p { opacity: 1; padding: 0.15em 0.3em 0.75em; }
.sx-benefit--shop .sx-button {
  position: relative;
  margin: 0;
  opacity: 0.35;
  transition: opacity 300ms var(--ease-out), transform 160ms var(--ease-out);
}
.s-ramp .sx-benefit--shop .sx-button { opacity: 1; }
.sx-shop__label,
.sx-shop__done {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4em;
  transition: opacity 300ms var(--ease-out), transform 500ms var(--ease-out);
}
.sx-shop__done { opacity: 0; transform: translateY(0.8em); }
.s-shop .sx-shop__label { opacity: 0; transform: translateY(-0.8em); }
.s-shop .sx-shop__done { opacity: 1; transform: none; }
.sx-shop__done svg {
  width: 1.25em;
  height: 1.25em;
  fill: none;
  stroke: #FFFFFF;
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.scene--winnie.s-out .sx-page,
.scene--winnie.s-out .sx-veil { opacity: 0; }
.scene--winnie.s-out .sx-success { transform: translateY(105%); }

/* -- Chapter 3: a weekend away for Bobbi --------------------------

   Opens on the chapter's photo as a lock screen: a notification drops
   in (s-notif), the cursor taps it, and Etho opens out of it (s-open,
   s-app) on three stays (s-list). The cabin's sheet rises (s-sheet);
   a pupcake flavour is picked (s-pumpkin), an outdoor dinner added
   (s-dinner), and the weekend booked (s-booked). */

.scene--trip::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(14, 15, 12, 0.4) 0%, rgba(14, 15, 12, 0.05) 38%, rgba(14, 15, 12, 0.05) 75%, rgba(14, 15, 12, 0.35) 100%);
  transition: opacity 500ms var(--ease-out);
}
.scene--trip.s-open::before { opacity: 0; }

/* The first frame is an iPhone lock screen over the chapter's photo:
   padlock, date and a large clock, the torch and camera buttons, and
   the home bar. It fades once the notification is opened. */
.sx-lock {
  position: absolute;
  inset: 0;
  color: #FFFFFF;
  text-align: center;
  text-shadow: 0 0.05em 0.6em rgba(0, 0, 0, 0.25);
  transition: opacity 450ms var(--ease-out);
}
.s-open .sx-lock { opacity: 0; }
.sx-lock__padlock {
  display: block;
  width: 1.15em;
  height: 1.15em;
  margin: 2.3em auto 0;
  fill: none;
  stroke: #FFFFFF;
  stroke-width: 2;
  stroke-linejoin: round;
}
.sx-lock__date {
  margin-top: 0.7em !important;
  font-size: 1.05em;
  font-weight: 600;
}
.sx-lock__time {
  margin-top: -0.05em !important;
  font-size: 5.4em;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.03em;
}
.sx-lock__btn {
  position: absolute;
  bottom: 2.6em;
  display: grid;
  place-items: center;
  width: 2.9em;
  height: 2.9em;
  border-radius: 50%;
  background: rgba(30, 30, 30, 0.35);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.sx-lock__btn--left { left: 2.4em; }
.sx-lock__btn--right { right: 2.4em; }
.sx-lock__btn svg {
  width: 1.3em;
  height: 1.3em;
  fill: none;
  stroke: #FFFFFF;
  stroke-width: 1.8;
  stroke-linejoin: round;
}
.sx-lock__bar {
  position: absolute;
  left: 50%;
  bottom: 0.7em;
  width: 7.5em;
  height: 0.28em;
  margin-left: -3.75em;
  border-radius: 1em;
  background: #FFFFFF;
}

.sx-notif {
  position: absolute;
  z-index: 2;
  top: 13.8em;
  left: 0.7em;
  right: 0.7em;
  display: flex;
  align-items: center;
  gap: 0.75em;
  padding: 0.8em 0.9em;
  border-radius: 1.5em;
  background: rgba(250, 249, 246, 0.82);
  backdrop-filter: blur(18px) saturate(1.5);
  -webkit-backdrop-filter: blur(18px) saturate(1.5);
  box-shadow: 0 0.5em 1.8em rgba(14, 15, 12, 0.22);
  opacity: 0;
  transform: translateY(1.5em) scale(0.94);
  transition: transform 750ms var(--ease-out), opacity 400ms var(--ease-out);
}
.s-notif .sx-notif { opacity: 1; transform: none; }
.sx-notif.is-pressed { transform: scale(0.97); transition-duration: 160ms; }
.s-open .sx-notif { opacity: 0; transform: scale(0.96); transition-duration: 400ms; }
.sx-notif__icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 2.5em;
  height: 2.5em;
  border-radius: 0.65em;
  background: #000000;
}
.sx-notif__icon svg { width: 1.45em; height: 1.45em; fill: #FFFFFF; }
.sx-notif > div { flex: 1; min-width: 0; }
.sx-notif__app {
  font-size: 0.72em;
  font-weight: 600;
  color: var(--sx-text2);
}
.sx-notif__app span { float: right; font-weight: 500; }
.sx-notif .sx-title { margin: 0.1em 0 0 !important; padding: 0; font-size: 0.92em; }
.sx-notif .sx-line { margin-top: 0.05em !important; font-size: 0.82em; }

/* Etho opens out of the notification, rather than rising from below. */
.scene__app--trip {
  padding-top: 1.2em;
  opacity: 0;
  transform: scale(0.9);
  transform-origin: 50% 33%;
  transition: transform 750ms var(--ease-out), opacity 450ms var(--ease-out), filter 600ms var(--ease-out);
}
.s-app .scene__app--trip { opacity: 1; transform: none; }
.scene__app--trip .sx-headline { margin-top: 1.2em !important; }
.sx-line--lead { margin-top: 0.3em !important; padding-right: 1.5em; font-size: 0.9em; }
.sx-tcards--trip { top: 7.6em; }
.scene img.sx-focus-cabin { object-position: 40% 62%; }
.scene img.sx-focus-hotel { object-position: 50% 62%; }
.scene img.sx-focus-farm { object-position: 50% 68%; }
.scene img.sx-focus-bed { object-position: 50% 55%; }

/* The cabin's sheet: taller than chapter 1's, it carries the stay,
   the pupcake and the dinner. */
.scene__sheet--trip { height: 33.5em; }
.sx-hero--trip { height: 9.6em; }
.sx-price__note { font-size: 0.75em; font-weight: 500; color: var(--sx-text2); }

/* The pupcake opens on a tap (s-cake) to show its flavours; once one
   is picked (s-pumpkin) it folds away again (s-caked) and the line
   names the choice. */
.sx-pupcake {
  margin-top: 1em;
  padding: 0.7em 0 0.75em;
  border-top: 1px solid var(--sx-rule);
  border-bottom: 1px solid var(--sx-rule);
}
.sx-pupcake__head {
  display: flex;
  align-items: center;
  gap: 0.8em;
  transition: transform 160ms var(--ease-out);
}
.sx-pupcake__head.is-pressed { transform: scale(0.98); }
.sx-pupcake__photo {
  flex: none;
  width: 4.3em;
  height: 4.3em;
  border-radius: 0.9em;
  overflow: hidden;
}
.sx-pupcake__head > div { flex: 1; min-width: 0; }
.sx-pupcake .sx-title { margin: 0 !important; padding: 0; }
.sx-pupcake .sx-title .sx-pill { margin-left: 0.3em; font-size: 0.78em; vertical-align: 0.1em; }
.sx-pupcake .sx-line { margin-top: 0.25em !important; }
.sx-pupcake__pick { display: none; font-weight: 700; color: var(--sx-title); }
.s-caked .sx-pupcake__ask { display: none; }
.s-caked .sx-pupcake__pick { display: inline; }
.sx-chevron {
  flex: none;
  width: 1.3em;
  height: 1.3em;
  fill: none;
  stroke: var(--sx-text2);
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 400ms var(--ease-out);
}
.s-cake .sx-chevron { transform: rotate(180deg); }
.s-caked .sx-chevron { transform: none; }
.sx-flavours {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 550ms var(--ease-out);
}
.sx-flavours > .sx-chips {
  overflow: hidden;
  padding-left: 5.1em;
  opacity: 0;
  transition: opacity 350ms var(--ease-out), padding-top 550ms var(--ease-out);
}
.s-cake .sx-flavours { grid-template-rows: 1fr; }
.s-cake .sx-flavours > .sx-chips { opacity: 1; padding-top: 0.65em; transition-delay: 120ms, 0ms; }
.s-caked .sx-flavours { grid-template-rows: 0fr; }
.s-caked .sx-flavours > .sx-chips { opacity: 0; padding-top: 0; transition-delay: 0ms; }
.sx-chips { display: flex; gap: 0.4em; }
.sx-chip {
  padding: 0.42em 0.8em;
  border-radius: 2em;
  border: 1.5px solid rgba(14, 15, 12, 0.14);
  font-size: 0.78em;
  font-weight: 600;
  transition: background 300ms var(--ease-out), color 300ms var(--ease-out), border-color 300ms var(--ease-out), transform 160ms var(--ease-out);
}
.sx-chip.is-pressed { transform: scale(0.92); }
.s-pumpkin [data-target="pumpkin"] { background: #000000; border-color: #000000; color: #FFFFFF; }

.scene--trip .sx-row { border-bottom: 0; }
.s-dinner [data-target="dinner"] .sx-check { background: #000000; border-color: #000000; }
.s-dinner [data-target="dinner"] .sx-check path { stroke-dashoffset: 0; }

.scene__sheet--trip .sx-button { position: relative; }
.sx-book__label,
.sx-book__done {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4em;
  transition: opacity 300ms var(--ease-out), transform 500ms var(--ease-out);
}
.sx-book__done { opacity: 0; transform: translateY(0.8em); }
.s-booked .sx-book__label { opacity: 0; transform: translateY(-0.8em); }
.s-booked .sx-book__done { opacity: 1; transform: none; }
.sx-book__done svg {
  width: 1.25em;
  height: 1.25em;
  fill: none;
  stroke: #FFFFFF;
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.sx-total--trip { margin-top: auto; }
.sx-total--trip + .sx-button { margin-top: 0; }
.sx-total--trip > p { padding-left: 0; padding-right: 0; }
.s-total .sx-total--trip > p { padding: 0.2em 0 0.7em; }

.scene--trip.s-out .scene__app--trip { opacity: 0; transform: none; filter: none; }
.scene--trip.s-out .scene__sheet { transform: translateY(110%); }

/* Chapter 3's success screen: the puppy's, with a confetti burst
   across the frame, the booking as a card, the saving counting up,
   and birthday outfits to shop along the foot. */
.sx-success--party .sx-cheer { font-size: 1.7em; }
.sx-booking--party {
  margin-top: 1.2em;
  opacity: 0;
  transform: translateY(0.8em);
  transition: opacity 400ms var(--ease-out) 550ms, transform 600ms var(--ease-out) 550ms;
}
.s-success .sx-booking--party { opacity: 1; transform: none; }
.sx-benefit--party {
  margin: 0.9em -0.75em 0;
  padding: 0.95em 1.3em;
}
.sx-benefit--party p { display: flex; justify-content: space-between; align-items: baseline; font-size: 1.15em; }
.sx-benefit--party b { font-size: 1.3em; font-variant-numeric: tabular-nums; }
.sx-outfits {
  margin-top: auto;
  padding-bottom: 0.9em;
  opacity: 0;
  transform: translateY(1.2em);
  transition: opacity 500ms var(--ease-out) 900ms, transform 700ms var(--ease-out) 900ms;
}
.s-success .sx-outfits { opacity: 1; transform: none; }
.sx-outfits__head { display: flex; justify-content: space-between; align-items: flex-end; }
.sx-outfits__head .sx-title { margin: 0 !important; padding: 0; }
.sx-outfits__head .sx-line { margin-top: 0.1em !important; }
.sx-outfits__shop { font-size: 0.85em; font-weight: 700; }
.sx-outfits__row { display: flex; gap: 0.6em; margin-top: 0.65em; }
.sx-outfit { flex: 1; min-width: 0; }
.sx-outfit__img {
  display: block;
  height: 9em;
  border-radius: 1em;
  overflow: hidden;
  background: #EFEDE8;
}
.scene img.sx-focus-hatbow { object-position: 50% 22%; }
.scene img.sx-focus-bandana { object-position: 55% 70%; }
.scene img.sx-focus-hat { object-position: 60% 25%; }
.sx-outfit .sx-title { margin-top: 0.45em !important; padding: 0; font-size: 0.8em; }
.sx-outfit .sx-line--price { padding: 0; font-size: 0.78em; }

.sx-confetti {
  position: absolute;
  inset: 0;
  z-index: 3;
  overflow: hidden;
  pointer-events: none;
  border-radius: inherit;
}
.sx-confetti span {
  position: absolute;
  top: -1em;
  left: var(--x);
  width: 0.42em;
  height: 0.7em;
  border-radius: 0.1em;
  background: var(--c);
  opacity: 0;
}
.s-success .sx-confetti span {
  animation: sx-confetti var(--t) var(--d) cubic-bezier(0.25, 0.6, 0.5, 1) both;
}
@keyframes sx-confetti {
  0% { opacity: 1; transform: translate3d(0, 0, 0) rotate(0) scale(var(--s)); }
  85% { opacity: 1; }
  100% { opacity: 0; transform: translate3d(1.5em, 44em, 0) rotate(var(--r)) scale(var(--s)); }
}
@media (prefers-reduced-motion: reduce) {
  .sx-confetti { display: none; }
}
.scene--trip.s-out .sx-success { transform: translateY(105%); }

/* -- The cursor ----------------------------------------------------- */

/* Its tip is its top-left corner, so placing the box places the tip.
   scenes.js sets --x/--y and the length of each glide; a glide eases
   in and out, as a hand does, while everything the cursor touches
   answers on --ease-out. */
.scene__cursor {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 3;
  width: 1.4em;
  height: auto;
  overflow: visible;
  opacity: 0;
  transform: translate(var(--x, 0), var(--y, 0)) scale(var(--press, 1));
  transform-origin: 0 0;
  transition:
    transform var(--glide, 800ms) cubic-bezier(0.65, 0, 0.35, 1),
    opacity 300ms var(--ease-out);
  filter: drop-shadow(0 0.12em 0.28em rgba(0, 0, 0, 0.28));
}
.scene__cursor path {
  fill: #000000;
  stroke: #FFFFFF;
  stroke-width: 1.4;
  stroke-linejoin: round;
}
.s-cursor .scene__cursor { opacity: 1; }
