/* ==========================================================================
   THE FOUNDING OF BABY ANGELS — interactive founder story
   Self-contained. Nothing here leaks into the rest of the site: every rule
   is scoped under .fs-root or a .fs- prefix.

   Architecture: ONE master nursery image as the environment, with the baby,
   props and UI living above it. The nursery is never rebuilt in 3D.
   Three.js is used only for the story bubble and its burst.

   The founder message is real HTML inside .fs-chapter — always in the DOM,
   readable with JavaScript off.
   ========================================================================== */

.fs-root {
  --fs-ivory:  #FBF7F2;
  --fs-blush:  #F4A5B8;
  --fs-rose:   #D47896;
  --fs-blue:   #BBC8E5;
  --fs-blued:  #8FA3C9;
  --fs-lav:    #CFC6E4;
  --fs-ink:    #2A2438;
  --fs-soft:   #4A4258;
  --fs-mute:   #7A7388;
  --fs-ease:   cubic-bezier(0.22, 0.61, 0.36, 1);
  /* One size for every word inside a cloud - the thought clouds and the
     reward cloud read as the same voice. (17 Aug 2026, Winnie) */
  --fs-cloud-fs: clamp(15px, 1.4vw, 20px);
  --fs-px: 0px;                      /* pointer parallax, set from JS */
  --fs-py: 0px;

  position: relative;
  padding: clamp(24px, 2.8vw, 40px) 0 clamp(48px, 6vw, 88px);
  overflow: hidden;
  font-family: var(--font, 'Inter', system-ui, sans-serif);
  /* The story's italics are a STORYBOOK italic — a warm book serif with
     calligraphic terminals — not a swirly script and not Playfair's sharp,
     high-contrast italic. Lora carries emphasis without shouting and still
     reads as something printed in a children's book. */
  --fs-script: 'Lora', Georgia, 'Times New Roman', serif;
}

/* ---------- section heading ---------- */
.fs-head { text-align: center; max-width: 720px; margin: 0 auto clamp(20px, 2.6vw, 34px); padding: 0 20px; }
.fs-eyebrow {
  display: inline-flex; align-items: center; gap: 10px;
  font-size: 11px; font-weight: 500; letter-spacing: .22em; text-transform: uppercase;
  color: var(--fs-blued);
}
.fs-eyebrow::before { content: ''; width: 26px; height: 1px; background: currentColor; }
.fs-head h2 {
  font-family: var(--font-display, 'Playfair Display', serif); font-weight: 500;
  font-size: clamp(27px, 3.4vw, 44px); line-height: 1.12; letter-spacing: -.015em;
  color: var(--fs-ink); margin: 14px 0 6px;
}
.fs-byline { font-family: var(--fs-script); font-style: italic; font-weight: 400; font-size: 16px; color: var(--fs-mute); margin: 0; }

/* ---------- the stage ---------- */
.fs-stage {
  position: relative; width: 100%; max-width: 1280px; margin: 0 auto;
  aspect-ratio: 3 / 2;                       /* the master image's own ratio */
  border-radius: clamp(16px, 2vw, 28px);
  overflow: hidden;
  background: #F6EFE9;
  box-shadow: 0 34px 68px -40px rgba(150, 124, 160, .55);
  isolation: isolate;
  touch-action: pan-y;
  /* a long chapter on a small screen is given room rather than a scrollbar */
  transition: min-height .65s var(--fs-ease);
}

/* The nursery. One image, never reconstructed. */
.fs-bg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 50%;
  z-index: 0;
  /* Cinematic rest state: a hair over 1 so the slow drift never exposes an
     edge. Pointer parallax is a few pixels at most — felt, not seen. */
  transform: scale(1.015) translate3d(var(--fs-px), var(--fs-py), 0);
  transition: transform 1.4s var(--fs-ease);
  will-change: transform;
}
/* The 46-second drift scaled the nursery from 1.015 to 1.028 and slid it 6px
   across — which, against a frame that does not move, reads as the frame
   creeping up and down on its own. It also slid the marks off the furniture
   they are meant to be standing on, since their perches are measured against
   the picture at rest. The room holds still now; the pointer parallax below
   is the only movement left, and that is a couple of pixels. */
.fs-root.is-live .fs-bg { animation: none; }
@keyframes fsDrift {
  0%, 100% { transform: scale(1.015) translate3d(calc(var(--fs-px)), calc(var(--fs-py)), 0); }
  50%      { transform: scale(1.028) translate3d(calc(var(--fs-px) + 6px), calc(var(--fs-py) - 4px), 0); }
}

/* A milestone's own room, crossfaded over the master nursery. Absent, this
   layer is simply never built and the nursery shows through. */
.fs-room {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-size: cover; background-position: 50% 50%;
  opacity: 0; transition: opacity 1.1s var(--fs-ease);
  transform: scale(1.015) translate3d(var(--fs-px), var(--fs-py), 0);
}
.fs-room.is-on { opacity: 1; }

/* A whisper of warm light over the whole room, so it breathes */
.fs-light {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: radial-gradient(120% 90% at 18% 22%, rgba(255,246,232,.20) 0%, rgba(255,246,232,0) 55%);
  mix-blend-mode: screen; opacity: .55;
  animation: fsGlow 18s ease-in-out infinite;
}
@keyframes fsGlow { 0%,100% { opacity: .45; } 50% { opacity: .68; } }

/* Vignette so overlaid text always has something to sit on */
.fs-vignette {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: radial-gradient(115% 85% at 50% 44%, rgba(255,253,250,0) 52%, rgba(250,242,238,.30) 100%);
}

/* Three.js bubbles. Promoted out of the stage by the script so they drift
   past the frame; above the artwork, below every control. */
/* Bubbles belong to the nursery, so they stay hidden behind the closed
   curtain and fade in as it parts. Once open they drift past the frame. */
.fs-fx {
  position: absolute; inset: 0; z-index: 3; pointer-events: none;
  opacity: 0; transition: opacity 1.6s var(--fs-ease) .55s;
}
.fs-root.is-open .fs-fx { opacity: 1; }
.fs-fx canvas { width: 100% !important; height: 100% !important; display: block; }

/* ---------- curtain ----------
   The nursery's own window voile, drawn across the stage. It sits above the
   room and the bubbles but below the intro text, so the opening lines read as
   though printed on the fabric. On "Begin Our Story" the panels part and fade.
   ---------------------------------------------------------------------- */
.fs-curtain { position: absolute; inset: 0; z-index: 4; pointer-events: none; overflow: hidden; }
.fs-curtain-panel {
  position: absolute; top: 0; bottom: 0; width: 56%;
  /* Cream voile: a wash of warm light, soft irregular folds, and a little
     more shade toward the outer edge where the fabric gathers. */
  background:
    linear-gradient(90deg, rgba(226,208,196,.26) 0%, rgba(255,255,255,0) 26%),
    linear-gradient(180deg, rgba(255,253,250,.99) 0%, rgba(252,246,241,.96) 55%, rgba(247,238,232,.97) 100%),
    repeating-linear-gradient(90deg,
      rgba(206,186,176,.15) 0px, rgba(206,186,176,.15) 2px,
      rgba(255,255,255,0)   2px,  rgba(255,255,255,0)  21px,
      rgba(206,186,176,.08) 21px, rgba(206,186,176,.08) 23px,
      rgba(255,255,255,0)   23px, rgba(255,255,255,0)  46px);
  will-change: transform, opacity;
  transition: transform 1.8s cubic-bezier(.45,0,.15,1), opacity 1.5s ease .4s;
}
/* The two halves overlap and dissolve into each other. Butted edge to edge
   with an inner shadow, they drew a hard line straight down the middle of the
   frame — the one thing a drawn curtain should never do. */
.fs-curtain-panel--l {
  left: 0;
  -webkit-mask-image: linear-gradient(90deg, #000 0%, #000 78%, rgba(0,0,0,0) 100%);
          mask-image: linear-gradient(90deg, #000 0%, #000 78%, rgba(0,0,0,0) 100%);
}
.fs-curtain-panel--r {
  right: 0; transform-origin: right center;
  background-image:
    linear-gradient(270deg, rgba(226,208,196,.26) 0%, rgba(255,255,255,0) 26%),
    linear-gradient(180deg, rgba(255,253,250,.99) 0%, rgba(252,246,241,.96) 55%, rgba(247,238,232,.97) 100%),
    repeating-linear-gradient(90deg,
      rgba(206,186,176,.15) 0px, rgba(206,186,176,.15) 2px,
      rgba(255,255,255,0)   2px,  rgba(255,255,255,0)  21px,
      rgba(206,186,176,.08) 21px, rgba(206,186,176,.08) 23px,
      rgba(255,255,255,0)   23px, rgba(255,255,255,0)  46px);
  -webkit-mask-image: linear-gradient(270deg, #000 0%, #000 78%, rgba(0,0,0,0) 100%);
          mask-image: linear-gradient(270deg, #000 0%, #000 78%, rgba(0,0,0,0) 100%);
}
/* A scalloped valance under the rail, so it reads as a hung curtain rather
   than two panes of cream. */
.fs-curtain::before {
  content: ''; position: absolute; left: -1%; right: -1%;
  top: clamp(9px, 1.5%, 15px); height: clamp(26px, 4.5%, 52px);
  background:
    radial-gradient(ellipse 50% 100% at 50% 0%, rgba(250,242,236,.98) 0%, rgba(250,242,236,.98) 62%, rgba(250,242,236,0) 64%)
      0 0 / 9% 100% repeat-x,
    linear-gradient(180deg, rgba(252,246,241,.98) 0%, rgba(250,242,236,.9) 60%, rgba(248,238,231,0) 100%);
  filter: drop-shadow(0 3px 5px rgba(150,124,140,.16));
  transition: opacity 1.1s ease .45s, transform 1.1s var(--fs-ease) .25s;
}
.fs-root.is-open .fs-curtain::before { opacity: 0; transform: translateY(-110%); }

/* The rail the fabric hangs from. Hard colour stops, not a soft wash — a
   dark top edge, one crisp specular band, mid body, then the underside in
   shadow. That sharp highlight line is what makes it read as a rod. */
.fs-curtain-rod {
  position: absolute; left: -0.5%; right: -0.5%; top: 0;
  height: clamp(9px, 1.5%, 15px); border-radius: 999px;
  background: linear-gradient(180deg,
    #B99A88 0%,   #C4A794 11%,
    #EBD9CC 12%,  #FBF1E8 22%,
    #EEDCCF 31%,
    #DCC3B3 55%,
    #C6A895 78%,
    #A98A78 93%,  #9C7C6A 100%);
  box-shadow: 0 4px 9px -3px rgba(120,96,110,.5), inset 0 0 0 .5px rgba(255,255,255,.35);
  transition: opacity 1.1s ease .5s, transform 1.1s var(--fs-ease) .3s;
}
/* finials at each end */
.fs-curtain-rod::before, .fs-curtain-rod::after {
  content: ''; position: absolute; top: 50%; width: 1.15em; height: 1.15em;
  transform: translateY(-50%); border-radius: 50%;
  background: radial-gradient(circle at 34% 28%, #FBF1E8 0%, #DCC3B3 46%, #A98A78 100%);
  box-shadow: 0 2px 5px -1px rgba(120,96,110,.5);
}
.fs-curtain-rod::before { left: -0.5em; }
.fs-curtain-rod::after  { right: -0.5em; }
.fs-root.is-open .fs-curtain-panel--l { transform: translateX(-101%); opacity: 0; }
.fs-root.is-open .fs-curtain-panel--r { transform: translateX(101%);  opacity: 0; }
.fs-root.is-open .fs-curtain-rod      { transform: translateY(-100%); opacity: 0; }
.fs-root.is-open .fs-curtain          { pointer-events: none; }
@media (prefers-reduced-motion: reduce) {
  .fs-curtain-panel, .fs-curtain-rod { transition: opacity .3s linear; }
  .fs-root.is-open .fs-curtain-panel--l,
  .fs-root.is-open .fs-curtain-panel--r { transform: none; }
}

/* ---------- overlays ---------- */
.fs-layer {
  position: absolute; inset: 0; z-index: 5;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: clamp(18px, 3vw, 44px);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .8s var(--fs-ease), visibility .8s;
}
.fs-layer.is-on { opacity: 1; visibility: visible; pointer-events: auto; }

/* ---------- intro ---------- */
.fs-intro { text-align: center; position: relative; }
/* The room is pale and busy, so the words need something to sit on. A soft
   radial wash of the page ivory, not a box — it reads as light, not a panel. */
.fs-intro::before {
  content: ''; position: absolute; z-index: -1;
  left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: min(140%, 900px); height: min(150%, 520px);
  background: radial-gradient(ellipse at center,
    rgba(255,252,249,.94) 0%, rgba(255,252,249,.80) 38%,
    rgba(255,252,249,.42) 62%, rgba(255,252,249,0) 82%);
  filter: blur(6px); pointer-events: none;
}
.fs-intro-line {
  opacity: 0; transform: translateY(14px);
  transition: opacity 1.1s var(--fs-ease), transform 1.1s var(--fs-ease);
}
.fs-intro-line.is-in { opacity: 1; transform: none; }
.fs-intro .fs-lead {
  /* Dancing Script is already the site's warm hand — it sets the tagline in
     styles.css. Script needs a size bump and more leading to read well. */
  font-family: 'Quicksand', 'Nunito', var(--font, sans-serif); font-style: normal;
  font-weight: 500;
  font-size: clamp(19px, 2.2vw, 28px); line-height: 1.5; letter-spacing: .002em; color: var(--fs-ink);
  margin: 0 0 12px; max-width: 20ch;
  text-shadow: 0 1px 2px rgba(255,253,250,1), 0 0 16px rgba(255,253,250,.95);
}
.fs-intro .fs-pull {
  font-family: 'Quicksand', 'Nunito', var(--font, sans-serif); font-style: normal;
  font-weight: 600;
  font-size: clamp(23px, 3vw, 38px); line-height: 1.34; letter-spacing: -.004em; color: #C2557E;
  margin: 0 0 clamp(22px, 3vw, 34px); border: 0; padding: 0;
  text-shadow: 0 1px 2px rgba(255,253,250,1), 0 0 18px rgba(255,253,250,.95);
}

/* ---------- buttons ---------- */
.fs-btn {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 16px 34px; border-radius: 12px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.9);
  font-family: inherit; font-size: 11px; font-weight: 600; letter-spacing: .2em; text-transform: uppercase;
  color: #2A2438; background: linear-gradient(100deg, #F7CBD9 0%, #EBD6E9 36%, #D9DEF1 68%, #CBDCEF 100%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.85), 0 12px 26px -12px rgba(120,100,140,.6);
  transition: transform .35s var(--fs-ease), filter .35s var(--fs-ease), box-shadow .35s var(--fs-ease);
}
.fs-btn:hover { transform: translateY(-2px); filter: saturate(1.09) brightness(1.02);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.95), 0 18px 34px -14px rgba(120,100,140,.7); }
.fs-btn:focus-visible, .fs-ms:focus-visible, .fs-ck:focus-visible, .fs-continue:focus-visible {
  outline: 2px solid var(--fs-rose); outline-offset: 3px;
}
.fs-btn--quiet {
  background: rgba(255,255,255,.82); color: var(--fs-soft);
  box-shadow: 0 10px 24px -14px rgba(120,100,130,.6); backdrop-filter: blur(8px);
}

/* ---------- milestone controls, arranged around the room ---------- */
.fs-hub { justify-content: flex-end; }
.fs-ms-wrap { position: absolute; inset: 0; }
.fs-ms {
  position: absolute; width: clamp(70px, 7.6vw, 98px); padding: 0; border: 0; cursor: pointer;
  background: none; text-align: center; color: var(--fs-soft); font-family: inherit;
  transition: transform .4s var(--fs-ease);
}
.fs-ms-face {
  width: clamp(56px, 5.9vw, 78px); height: clamp(56px, 5.9vw, 78px); margin: 0 auto 8px;
  border-radius: 50%; display: flex; align-items: center; justify-content: center;
  border: none;
  /* A soap bubble, not a white disc. Roundness comes from shading, never an
     outline: tight specular, broad sheen, bounce light returning on the lower
     right, curvature falling away from the key, and a rim vignette. The room
     behind shows through, which is what makes it read as glass. */
  background:
    radial-gradient(ellipse 22% 16% at 30% 20%,
      rgba(255,255,255,.98) 0%, rgba(255,255,255,.42) 42%, rgba(255,255,255,0) 100%),
    radial-gradient(circle at 30% 26%,
      rgba(255,255,255,.44) 0%, rgba(255,255,255,.12) 28%, rgba(255,255,255,0) 56%),
    radial-gradient(circle at 76% 82%,
      rgba(255,255,255,.40) 0%, rgba(255,255,255,0) 34%),
    radial-gradient(circle at 68% 74%,
      rgba(186,172,204,.16) 0%, rgba(186,172,204,0) 60%),
    radial-gradient(circle at 50% 50%,
      rgba(146,130,168,0) 56%, rgba(146,130,168,.10) 82%, rgba(146,130,168,.19) 100%),
    rgba(255,255,255,.19);
  -webkit-backdrop-filter: blur(7px) saturate(1.3);
          backdrop-filter: blur(7px) saturate(1.3);
  box-shadow:
    inset -8px -10px 20px rgba(150,135,175,.15),
    inset 8px 9px 18px rgba(255,255,255,.52),
    0 0 0 1px rgba(255,255,255,.40),
    0 14px 26px -13px rgba(120,100,140,.55);
  transition: all .42s var(--fs-ease);
  /* A little lift and a hint of tilt: they read as glass floating above the
     nursery rather than stickers printed on it. */
  animation: fsMarkFloat 6.5s ease-in-out infinite;
  transform-style: preserve-3d;
}
@keyframes fsMarkFloatSm {
  0%, 100% { transform: translateY(0)    rotateX(0deg); }
  50%      { transform: translateY(-5px) rotateX(6deg); }
}
@keyframes fsMarkFloat {
  0%, 100% { transform: translateY(0)    rotateX(0deg); }
  50%      { transform: translateY(-8px) rotateX(7deg); }
}
.fs-ms:nth-child(1) .fs-ms-face { animation-delay: 0s; }
.fs-ms:nth-child(2) .fs-ms-face { animation-delay: .55s; }
.fs-ms:nth-child(3) .fs-ms-face { animation-delay: 1.1s; }
.fs-ms:nth-child(4) .fs-ms-face { animation-delay: 1.65s; }
.fs-ms:nth-child(5) .fs-ms-face { animation-delay: 2.2s; }
.fs-ms-face svg {
  width: 60%; height: 60%; color: #33284A;      /* dark purple, reads on pale glass */
  /* the glyph sits inside the glass, so it catches a little of the shell */
  filter: drop-shadow(0 1px 1.5px rgba(255,255,255,.98)) drop-shadow(0 0 6px rgba(255,255,255,.7));
  transition: color .35s var(--fs-ease), transform .42s var(--fs-ease);
}
.fs-ms-label {
  display: block; font-size: 10.5px; letter-spacing: .13em; text-transform: uppercase;
  color: var(--fs-ink); font-weight: 500; text-shadow: 0 1px 3px rgba(255,255,255,1), 0 0 10px rgba(255,255,255,.9);
  transition: color .3s var(--fs-ease);
}
/* A mark never travels. It used to lift on hover, and because that rule
   replaced the whole transform it also threw away the centring shift, so a
   tap on a phone (where hover sticks) slid the ball half its own width to the
   right. The bubble now answers a touch with light and a burst, not movement. */
.fs-ms:hover { transform: translateX(-50%); }
.fs-ms:hover .fs-ms-face {
  /* the bubble catches more light and picks up a blush tint in the film */
  background:
    radial-gradient(ellipse 24% 18% at 29% 19%,
      rgba(255,255,255,1) 0%, rgba(255,255,255,.5) 40%, rgba(255,255,255,0) 100%),
    radial-gradient(circle at 30% 26%,
      rgba(255,255,255,.52) 0%, rgba(255,255,255,.16) 28%, rgba(255,255,255,0) 56%),
    radial-gradient(circle at 76% 82%,
      rgba(255,236,243,.52) 0%, rgba(255,236,243,0) 36%),
    radial-gradient(circle at 68% 74%,
      rgba(212,120,150,.14) 0%, rgba(212,120,150,0) 62%),
    radial-gradient(circle at 50% 50%,
      rgba(146,130,168,0) 54%, rgba(146,130,168,.11) 82%, rgba(146,130,168,.20) 100%),
    rgba(255,255,255,.27);
  box-shadow:
    inset -8px -10px 22px rgba(150,135,175,.16),
    inset 9px 10px 20px rgba(255,255,255,.62),
    0 0 0 1px rgba(255,255,255,.62),
    0 0 0 7px rgba(244,165,184,.13),
    0 20px 34px -14px rgba(212,120,150,.6);
}
.fs-ms:hover .fs-ms-face svg { transform: scale(1.06); }
.fs-ms.is-done .fs-ms-face {
  /* completed: the film cools to powder blue, still glass */
  background:
    radial-gradient(ellipse 22% 16% at 30% 20%,
      rgba(255,255,255,.98) 0%, rgba(255,255,255,.44) 42%, rgba(255,255,255,0) 100%),
    radial-gradient(circle at 76% 82%,
      rgba(226,238,255,.55) 0%, rgba(226,238,255,0) 36%),
    radial-gradient(circle at 50% 50%,
      rgba(143,163,201,0) 54%, rgba(143,163,201,.14) 82%, rgba(143,163,201,.24) 100%),
    rgba(233,240,251,.34);
  box-shadow:
    inset -8px -10px 20px rgba(143,163,201,.20),
    inset 8px 9px 18px rgba(255,255,255,.58),
    0 0 0 1px rgba(255,255,255,.5),
    0 14px 26px -13px rgba(120,130,160,.5);
}
.fs-ms.is-done .fs-ms-face svg { color: #4A4A72; }

/* The mark you have chosen — the console below names it, and Begin starts it.
   A brighter bubble and a soft ring, never a hard outline. */
.fs-ms.is-picked .fs-ms-face {
  box-shadow:
    inset -8px -10px 20px rgba(150,135,175,.14),
    inset 8px 9px 18px rgba(255,255,255,.68),
    0 0 0 1px rgba(255,255,255,.7),
    0 0 0 6px rgba(233,166,190,.24),
    0 18px 32px -14px rgba(120,100,140,.6);
  /* it does NOT move — a bubble that jumps when touched reads as a button */
}
.fs-ms.is-picked .fs-ms-face svg { color: #C4557C; transform: scale(1.06); }
.fs-ms.is-picked .fs-ms-label { color: #B84E72; }
.fs-ms-halo {
  position: absolute; inset: -7px; border-radius: 50%; pointer-events: none;
  border: 1.5px solid rgba(212,120,150,.55); opacity: 0; transform: scale(.8);
  transition: opacity .5s var(--fs-ease), transform .5s var(--fs-ease);
}
.fs-ms.is-done .fs-ms-halo { opacity: 1; transform: scale(1); }
.fs-ms-face { position: relative; }

/* placement around the furniture, in % of the stage */
/* ---------- the milestone rail ----------
   These used to be scattered across the nursery like a constellation, which
   was pretty but unreadable now that they run 01 to 05 in order — and the
   left-most one fell off the edge of the frame entirely. One centred row,
   in sequence, is what a numbered progression asks for. */
.fs-ms-wrap {
  perspective: 1000px;                 /* the marks hang in front of the room */
  inset: auto; left: 50%; top: clamp(16px, 5.5%, 54px);
  transform: translateX(-50%);
  display: flex; align-items: flex-start; justify-content: center;
  width: min(94%, 900px); gap: clamp(6px, 1.6vw, 26px);
}
.fs-ms { position: relative; left: auto; right: auto; top: auto; flex: 0 0 auto; padding-top: 15px; }
/* A gentle arch rather than a ruler-straight row — the marks rise toward the
   middle the way a mobile hangs over a cot. Set with margin, not transform,
   so the hover and selected lifts still work on top of it. */
/* Five bubbles at five depths, not a row of buttons. No two are the same
   size and no two sit on the same line — the bigger ones read as nearer, the
   smaller ones as further back, and each drifts on its own beat. */
.fs-ms:nth-child(1) { margin-top: clamp(26px, 4.0vw, 54px); }
.fs-ms:nth-child(2) { margin-top: 0; }
.fs-ms:nth-child(3) { margin-top: clamp(34px, 5.2vw, 70px); }
.fs-ms:nth-child(4) { margin-top: clamp(8px, 1.3vw, 17px); }
.fs-ms:nth-child(5) { margin-top: clamp(30px, 4.6vw, 62px); }

.fs-ms:nth-child(1) .fs-ms-face { width: clamp(58px, 6.1vw, 82px); height: clamp(58px, 6.1vw, 82px); }
.fs-ms:nth-child(2) .fs-ms-face { width: clamp(46px, 4.8vw, 64px); height: clamp(46px, 4.8vw, 64px); }
.fs-ms:nth-child(3) .fs-ms-face { width: clamp(66px, 7.0vw, 94px); height: clamp(66px, 7.0vw, 94px); }
.fs-ms:nth-child(4) .fs-ms-face { width: clamp(50px, 5.2vw, 70px); height: clamp(50px, 5.2vw, 70px); }
.fs-ms:nth-child(5) .fs-ms-face { width: clamp(60px, 6.4vw, 86px); height: clamp(60px, 6.4vw, 86px); }
/* the ones further back sit back in the light, too */
.fs-ms:nth-child(2) .fs-ms-face,
.fs-ms:nth-child(4) .fs-ms-face { opacity: .9; }
/* each on its own rise, so the five never breathe together */
.fs-ms:nth-child(1) .fs-ms-face { animation-duration: 6.5s; }
.fs-ms:nth-child(2) .fs-ms-face { animation-duration: 8.2s; }
.fs-ms:nth-child(3) .fs-ms-face { animation-duration: 5.6s; }
.fs-ms:nth-child(4) .fs-ms-face { animation-duration: 7.4s; }
.fs-ms:nth-child(5) .fs-ms-face { animation-duration: 6.9s; }

/* ---------- the tiny nursery console ---------- */
.fs-console {
  position: absolute; left: 50%; bottom: clamp(14px, 2.4vw, 26px); transform: translateX(-50%);
  z-index: 5; width: min(330px, 84%);
  padding: 14px 18px 16px; border-radius: 20px; text-align: center;
  background: rgba(255,255,255,.76);
  border: 1px solid rgba(255,255,255,.92);
  box-shadow: 0 20px 44px -22px rgba(120,100,140,.75), inset 0 1px 0 rgba(255,255,255,.95);
  backdrop-filter: blur(14px) saturate(1.15);
}
.fs-ck-brand { display: flex; align-items: center; justify-content: center; }
.fs-ck-logo {
  width: clamp(84px, 9vw, 108px); height: auto; display: block;
  filter: drop-shadow(0 1px 2px rgba(255,255,255,.9));
}
.fs-ck-name {
  font-family: 'Quicksand', 'Nunito', var(--font, sans-serif); font-weight: 600;
  font-size: clamp(16px, 1.7vw, 20px);
  color: var(--fs-ink); margin: 7px 0 9px; min-height: 1.2em;
  white-space: nowrap;
}
.fs-ck-row { display: flex; align-items: center; justify-content: center; gap: 14px; }
.fs-ck {
  width: 32px; height: 32px; border-radius: 50%; border: 1px solid rgba(42,36,56,.12);
  background: rgba(255,255,255,.8); color: var(--fs-soft); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: all .3s var(--fs-ease);
}
.fs-ck:hover { color: var(--fs-rose); border-color: var(--fs-blush); transform: translateY(-1px); }
.fs-ck-dots { display: flex; gap: 6px; }
.fs-ck-dot { width: 6px; height: 6px; border-radius: 50%; background: rgba(42,36,56,.18); transition: all .3s var(--fs-ease); }
.fs-ck-dot.is-on { background: var(--fs-rose); transform: scale(1.4); }
.fs-ck-dot.is-done { background: var(--fs-blued); }
.fs-ck-begin {
  margin-top: 12px; width: 100%; padding: 13px 0; cursor: pointer;
  white-space: nowrap;
  border: 1px solid rgba(255,255,255,.9); border-radius: 12px;
  font-family: inherit; font-size: 10.5px; font-weight: 600; letter-spacing: .2em; text-transform: uppercase;
  color: #2A2438; background: linear-gradient(100deg, #F7CBD9 0%, #EBD6E9 36%, #D9DEF1 68%, #CBDCEF 100%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.85), 0 10px 20px -12px rgba(120,100,140,.55);
  transition: transform .3s var(--fs-ease), filter .3s var(--fs-ease), box-shadow .3s var(--fs-ease);
}
.fs-ck-begin:hover { transform: translateY(-1px); filter: saturate(1.09) brightness(1.02);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.95), 0 14px 26px -12px rgba(120,100,140,.62); }
.fs-ck-begin[disabled] { opacity: .45; cursor: default; transform: none; }

/* ---------- journey indicator ---------- */
.fs-journey {
  display: flex; align-items: center; justify-content: center; gap: 0;
  margin: clamp(18px, 2.4vw, 28px) auto 0; padding: 0; list-style: none;
}
.fs-jp { display: flex; align-items: center; }
.fs-jp-dot {
  width: 13px; height: 13px; border-radius: 50%;
  border: 1.5px solid rgba(212,120,150,.42); background: rgba(255,255,255,.7);
  transition: all .6s var(--fs-ease);
}
.fs-jp.is-done .fs-jp-dot {
  /* blue into pink, the way the rest of the brand runs */
  background: linear-gradient(135deg, #BBD0EE 0%, #D8CDE9 52%, #F4B6C7 100%);
  border-color: transparent;
  box-shadow: 0 0 0 5px rgba(196,190,226,.20);
}
.fs-jp-dot { border-color: rgba(170,178,214,.45); }
.fs-jp-line {
  width: clamp(22px, 4vw, 46px); height: 1.5px; border-radius: 2px;
  background: linear-gradient(90deg, rgba(170,190,226,.30), rgba(226,182,200,.30));
}
.fs-jp.is-done .fs-jp-line {
  background: linear-gradient(90deg, rgba(155,182,226,.75), rgba(226,166,190,.75));
}
.fs-journey-heart { margin-left: 10px; color: rgba(212,120,150,.32); transition: all .8s var(--fs-ease); }
.fs-journey-heart svg { width: 19px; height: 19px; display: block; }
.fs-root.is-complete .fs-journey-heart { color: var(--fs-rose); transform: scale(1.16); }
.fs-journey-cap {
  display: block; text-align: center; font-size: 9.5px; letter-spacing: .2em;
  text-transform: uppercase; color: var(--fs-mute); margin-top: 10px;
}

/* ---------- milestone play area (Phase 6 scaffold) ---------- */
.fs-play { position: absolute; inset: 0; z-index: 4; pointer-events: none; }
.fs-play.is-on { pointer-events: auto; }
.fs-slot {
  position: absolute; pointer-events: none;
  background-size: contain; background-repeat: no-repeat; background-position: center bottom;
}
/* the baby sits on the open carpet, centre-low, where the room's light falls */
/* Brought forward — larger and lower in the frame, so she reads as sitting
   near the viewer rather than back in the room. Everything that meets her (the
   bottle's resting spot, her mouth, the thought cloud) is measured as a
   fraction of her artwork, so it all scales with her. (16 Aug 2026, Winnie) */
.fs-slot--baby   { left: 50%; bottom: 2%; width: 38%; height: 58%; transform: translateX(-50%); }
/* z-index 2 -> 7 (24 Aug 2026, Winnie: "the duck goes transparent and the
   nurse purple shirt is overlap the duck").

   The prop the visitor drags was painting at z-index 2, but a scene layer takes
   its own z from the milestone config and the bath's caregiver is z: 6 — so she
   was drawn OVER the duck. And because .fs-carer's lower edge is a FADING mask
   (solid to 88%, then falling to transparent by 97%), what landed on top of the
   duck was a half-transparent wash of her uniform: the duck looked see-through
   when in fact it was underneath her.

   The prop is the thing being carried, so it belongs in front of the furniture
   and the people. 7 puts it above every scene layer while still letting the
   thought cloud (also 7, but later in the DOM) sit over it. The nappy's `tuck`
   still sets z-index 0 inline when it is put away, and inline wins. */
.fs-slot--bottle { width: 6.4%; height: 20%; left: 76%; bottom: 30%; cursor: grab; pointer-events: auto;
  transition: filter .4s var(--fs-ease); z-index: 7; touch-action: none; }
.fs-slot--bottle.is-hot { filter: drop-shadow(0 0 14px rgba(255,214,230,.95)); animation: fsPulse 2.4s ease-in-out infinite; }
.fs-slot--bottle.is-held { cursor: grabbing; animation: none; }
/* A held bottle is carried nearly upright; only on contact does it swing down
   to the angle a caregiver actually feeds at — teat low, base high, so the
   teat stays full. The long ease is what makes it read as a hand, not a snap. */
.fs-slot--bottle { transform-origin: 42% 22%; }
.fs-slot--bottle.is-held:not(.is-pouring) { transform: rotate(-12deg); }
/* Fed. The bottle is lowered out of frame rather than left lying across her
   face — the happy expression is the payoff and must be unobstructed. */
.fs-slot--bottle.is-spent {
  transition: opacity .9s var(--fs-ease) .35s, transform 1.1s var(--fs-ease) .25s;
  opacity: 0;
  transform: rotate(104deg) translate(16%, 26%) scale(.94);
}
.fs-slot--bottle.is-spent .fs-hand { opacity: 0; }
.fs-slot--bottle.is-feeding {
  transition: left 1.05s cubic-bezier(.32,.72,.34,1),
              bottom 1.05s cubic-bezier(.32,.72,.34,1),
              transform 1.15s cubic-bezier(.32,.72,.34,1);
}
@keyframes fsPulse { 0%,100% { filter: drop-shadow(0 0 8px rgba(255,214,230,.6)); } 50% { filter: drop-shadow(0 0 20px rgba(255,200,222,1)); } }

/* soft contact shadow so the baby is standing in the room, not floating on it */
.fs-contact {
  position: absolute; left: 50%; bottom: 2.5%; width: 26%; height: 4%;
  transform: translateX(-50%); border-radius: 50%; pointer-events: none;
  background: radial-gradient(ellipse at center, rgba(150,124,110,.26) 0%, rgba(150,124,110,.08) 48%, rgba(150,124,110,0) 74%);
  filter: blur(6px);
}

/* Bottle = two registered layers derived from milk_bottle.png.
   .fs-glass is the bottle with the milk painted out; .fs-milk is the milk on
   its own. Clipping the milk from the top makes it genuinely drain, and a
   meniscus is drawn at the moving surface. */
.fs-glass, .fs-milk, .fs-hand {
  position: absolute; inset: 0;
  background-size: contain; background-repeat: no-repeat; background-position: center center;
}
/* The milk artwork is a silhouette of the WHOLE bottle, so painted over the
   glass it buried every wall and rim. Masked to the bottle's interior (the path
   shipped as story-art/bottle-interior.svg, inlined here so it cannot fail to
   load over file://). (16 Aug 2026, Winnie) */
.fs-milk {
  transition: clip-path .18s linear;
  -webkit-mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20640%201489'%3E%3Cpath%20d='M%20243.8%20283.1%20L%20231.8%20305.5%20L%20215.0%20328.8%20L%20194.9%20352.0%20L%20145.2%20375.3%20L%20107.5%20397.7%20L%2091.5%20421.0%20L%2085.9%20444.2%20L%2077.1%20467.5%20L%2071.4%20490.7%20L%2070.6%20513.2%20L%2070.6%20536.4%20L%2072.2%20559.7%20L%20111.5%20582.9%20L%2091.5%20605.4%20L%2075.4%20628.6%20L%2064.2%20651.9%20L%2058.6%20675.1%20L%2057.8%20698.4%20L%2058.6%20720.8%20L%2060.2%20744.1%20L%2062.6%20767.4%20L%2065.0%20790.6%20L%2066.6%20813.9%20L%2068.2%20836.3%20L%2070.6%20859.6%20L%2071.4%20882.8%20L%2072.2%20906.1%20L%2073.0%20928.5%20L%2073.0%20951.8%20L%2073.0%20975.0%20L%2073.0%20998.3%20L%2072.2%201021.5%20L%2071.4%201044.0%20L%2069.8%201067.2%20L%2067.4%201090.5%20L%2065.0%201113.7%20L%2062.6%201137.0%20L%2058.6%201159.4%20L%2057.0%201182.7%20L%2053.0%201205.9%20L%2050.6%201229.2%20L%2049.0%201251.6%20L%2049.0%201274.9%20L%2049.8%201298.1%20L%2053.0%201321.4%20L%2057.8%201344.7%20L%2065.8%201367.1%20L%2077.1%201390.4%20L%2092.3%201413.6%20L%20113.9%201436.9%20L%20159.6%201460.1%20L%20487.6%201460.1%20L%20527.7%201436.9%20L%20548.5%201413.6%20L%20562.1%201390.4%20L%20572.6%201367.1%20L%20580.6%201344.7%20L%20585.4%201321.4%20L%20587.0%201298.1%20L%20588.6%201274.9%20L%20587.8%201251.6%20L%20586.2%201229.2%20L%20584.6%201205.9%20L%20580.6%201182.7%20L%20577.4%201159.4%20L%20575.0%201137.0%20L%20572.6%201113.7%20L%20570.2%201090.5%20L%20567.8%201067.2%20L%20567.8%201044.0%20L%20566.2%201021.5%20L%20565.4%20998.3%20L%20564.6%20975.0%20L%20565.4%20951.8%20L%20566.2%20928.5%20L%20567.0%20906.1%20L%20567.8%20882.8%20L%20569.4%20859.6%20L%20571.8%20836.3%20L%20574.2%20813.9%20L%20576.6%20790.6%20L%20579.8%20767.4%20L%20582.2%20744.1%20L%20583.8%20720.8%20L%20584.6%20698.4%20L%20582.2%20675.1%20L%20576.6%20651.9%20L%20567.0%20628.6%20L%20549.3%20605.4%20L%20530.1%20582.9%20L%20567.0%20559.7%20L%20568.6%20536.4%20L%20568.6%20513.2%20L%20568.6%20490.7%20L%20564.6%20467.5%20L%20553.3%20444.2%20L%20547.7%20421.0%20L%20531.7%20397.7%20L%20498.8%20375.3%20L%20445.1%20352.0%20L%20425.0%20328.8%20L%20408.2%20305.5%20L%20397.0%20283.1%20Z'%20fill='%23000'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20640%201489'%3E%3Cpath%20d='M%20243.8%20283.1%20L%20231.8%20305.5%20L%20215.0%20328.8%20L%20194.9%20352.0%20L%20145.2%20375.3%20L%20107.5%20397.7%20L%2091.5%20421.0%20L%2085.9%20444.2%20L%2077.1%20467.5%20L%2071.4%20490.7%20L%2070.6%20513.2%20L%2070.6%20536.4%20L%2072.2%20559.7%20L%20111.5%20582.9%20L%2091.5%20605.4%20L%2075.4%20628.6%20L%2064.2%20651.9%20L%2058.6%20675.1%20L%2057.8%20698.4%20L%2058.6%20720.8%20L%2060.2%20744.1%20L%2062.6%20767.4%20L%2065.0%20790.6%20L%2066.6%20813.9%20L%2068.2%20836.3%20L%2070.6%20859.6%20L%2071.4%20882.8%20L%2072.2%20906.1%20L%2073.0%20928.5%20L%2073.0%20951.8%20L%2073.0%20975.0%20L%2073.0%20998.3%20L%2072.2%201021.5%20L%2071.4%201044.0%20L%2069.8%201067.2%20L%2067.4%201090.5%20L%2065.0%201113.7%20L%2062.6%201137.0%20L%2058.6%201159.4%20L%2057.0%201182.7%20L%2053.0%201205.9%20L%2050.6%201229.2%20L%2049.0%201251.6%20L%2049.0%201274.9%20L%2049.8%201298.1%20L%2053.0%201321.4%20L%2057.8%201344.7%20L%2065.8%201367.1%20L%2077.1%201390.4%20L%2092.3%201413.6%20L%20113.9%201436.9%20L%20159.6%201460.1%20L%20487.6%201460.1%20L%20527.7%201436.9%20L%20548.5%201413.6%20L%20562.1%201390.4%20L%20572.6%201367.1%20L%20580.6%201344.7%20L%20585.4%201321.4%20L%20587.0%201298.1%20L%20588.6%201274.9%20L%20587.8%201251.6%20L%20586.2%201229.2%20L%20584.6%201205.9%20L%20580.6%201182.7%20L%20577.4%201159.4%20L%20575.0%201137.0%20L%20572.6%201113.7%20L%20570.2%201090.5%20L%20567.8%201067.2%20L%20567.8%201044.0%20L%20566.2%201021.5%20L%20565.4%20998.3%20L%20564.6%20975.0%20L%20565.4%20951.8%20L%20566.2%20928.5%20L%20567.0%20906.1%20L%20567.8%20882.8%20L%20569.4%20859.6%20L%20571.8%20836.3%20L%20574.2%20813.9%20L%20576.6%20790.6%20L%20579.8%20767.4%20L%20582.2%20744.1%20L%20583.8%20720.8%20L%20584.6%20698.4%20L%20582.2%20675.1%20L%20576.6%20651.9%20L%20567.0%20628.6%20L%20549.3%20605.4%20L%20530.1%20582.9%20L%20567.0%20559.7%20L%20568.6%20536.4%20L%20568.6%20513.2%20L%20568.6%20490.7%20L%20564.6%20467.5%20L%20553.3%20444.2%20L%20547.7%20421.0%20L%20531.7%20397.7%20L%20498.8%20375.3%20L%20445.1%20352.0%20L%20425.0%20328.8%20L%20408.2%20305.5%20L%20397.0%20283.1%20Z'%20fill='%23000'/%3E%3C/svg%3E");
  -webkit-mask-size: contain;    mask-size: contain;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
.fs-milk::after {                       /* the surface of the milk */
  content: ''; position: absolute; left: 0; right: 0; height: 1.6%;
  top: var(--fs-surface, 20%);
  background: linear-gradient(180deg, rgba(255,255,252,.92) 0%, rgba(236,229,212,.55) 60%, rgba(236,229,212,0) 100%);
  pointer-events: none;
  /* Only while it is actually being drunk — a meniscus across a still bottle
     reads as a line ruled over the photograph. (16 Aug 2026, Winnie) */
  opacity: 0;
  transition: opacity .3s var(--fs-ease);
}
.fs-slot--bottle.is-pouring .fs-milk::after { opacity: 1; }
.fs-milk.is-empty { opacity: 0; }
.fs-milk.is-empty::after { opacity: 0; }
/* the caregiver's hand travels with the bottle */
.fs-hand { transform: scaleX(-1) translate(-52%, 26%) scale(2.1); transform-origin: 50% 40%;
  opacity: 0; transition: opacity .45s var(--fs-ease); pointer-events: none; }
.fs-slot--bottle.is-held .fs-hand,
.fs-slot--bottle.is-pouring .fs-hand { opacity: 1; }
/* a small lean toward the bottle while she drinks */
.fs-slot--baby.is-sipping { transition: transform 1.1s var(--fs-ease); transform: translateX(-50%) rotate(-2.2deg); }

/* scaffold marker — shown ONLY while real artwork is missing */
.fs-pending {
  position: absolute; inset: 0; border: 2px dashed rgba(212,120,150,.5); border-radius: 14px;
  background: rgba(255,255,255,.5);
  display: flex; align-items: center; justify-content: center; text-align: center;
  font-size: 9.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--fs-rose);
  padding: 6px; line-height: 1.5;
}

/* What to do next — spoken as a little dreamy cloud rather than a chat pill.
   The cloud is one inline SVG stretched to whatever the wording needs, so no
   raster asset is added and it never distorts the text. Its two trailing
   puffs are fixed-size, so they stay round at any width. */
.fs-cue {
  position: absolute; left: 50%; top: 19%; transform: translateX(-50%);
  padding: clamp(10px, 1.1vw, 16px) clamp(14px, 1.4vw, 22px);
  white-space: nowrap; max-width: 92%;
  /* No cloud. The words simply fade up out of the room, lifted off it by a
     soft edgeless halo rather than sat inside a drawn shape. */
  /* the halo is gone: the cloud on ::before is the surface now */
  background: none;
  text-shadow:
    0 1px 0 rgba(255,255,255,.95),
    0 0 14px rgba(255,253,250,.95),
    0 0 30px rgba(255,253,250,.85),
    0 6px 22px rgba(196,176,206,.35);
  font-size: var(--fs-cloud-fs); font-weight: 600; letter-spacing: .006em;
  color: #453C58;
  transition: opacity .5s var(--fs-ease), transform .5s var(--fs-ease);
  z-index: 7;
  /* It is a caption, not a control. Without this the enlarged cloud sits over
     the play area and swallows any drag that passes beneath it. */
  pointer-events: none;
}
/* ---------------------------------------------------------------------------
   THE INSTRUCTION IS THE BABY'S THOUGHT (16 Aug 2026, Winnie)
   A baby cannot ask for the bottle, so each prompt is something the baby is
   thinking: the words sit in a cloud with a tail of two shrinking puffs falling
   toward the baby below. Built from soft overlapping blobs rather than a hard
   mask, so the rim is uneven and soft like vapour instead of a drawn shape.
   One rule covers all five milestones — they share this element.
   --------------------------------------------------------------------------- */
.fs-cue::before {
  background-image:
    linear-gradient(180deg, rgba(255,255,255,.72) 0%, rgba(255,255,255,.86) 42%, rgba(255,255,255,.70) 100%),
    url('Image/dreamy-cloud-web.jpg');
  background-size: cover, cover;
  background-position: center, center;
  background-repeat: no-repeat, no-repeat;
  background-color: transparent;
  content: ''; position: absolute;
  /* Roomier. At -1.5/-0.9 the scallops closed in on the words and the text
     read as crammed; the cloud now carries a clear margin all round.
     (16 Aug 2026, Winnie) */
  /* Widened and flattened: the words are given room sideways so a sentence
     takes fewer lines, and the cloud stops growing downward over her head.
     (17 Aug 2026, Winnie) */
  inset: -1.7em -3.1em -1.6em;
  z-index: -2;
  pointer-events: none;
  /* A real cloud silhouette: one body with eleven lobes set right round it —
     over the top, down both sides and along the underneath — so every edge is a
     curve and there is no straight run anywhere. Soft overlapping gradients gave
     a fuzzy blob with no read to it; this keeps actual scalloped edges. The
     shape is a mask so the fill stays flat and clean.
     (16 Aug 2026, Winnie) */
  /* Lit from the upper left and falling away to a faint shade at the lower
     right, so the cloud reads as a rounded form rather than a flat cut-out.
     (16 Aug 2026, Winnie) */
  -webkit-mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20200%20120'%20preserveAspectRatio='none'%3E%3Cg%20fill='%23000'%3E%3Cellipse%20cx='100'%20cy='60'%20rx='56'%20ry='30'/%3E%3Ccircle%20cx='100.0'%20cy='30.0'%20r='15.9'/%3E%3Ccircle%20cx='121.4'%20cy='32.3'%20r='14.6'/%3E%3Ccircle%20cx='139.6'%20cy='38.8'%20r='12.7'/%3E%3Ccircle%20cx='151.7'%20cy='48.5'%20r='12.9'/%3E%3Ccircle%20cx='156.0'%20cy='60.0'%20r='15.7'/%3E%3Ccircle%20cx='151.7'%20cy='71.5'%20r='17.8'/%3E%3Ccircle%20cx='139.6'%20cy='81.2'%20r='16.3'/%3E%3Ccircle%20cx='121.4'%20cy='87.7'%20r='12.7'/%3E%3Ccircle%20cx='100.0'%20cy='90.0'%20r='10.7'/%3E%3Ccircle%20cx='78.6'%20cy='87.7'%20r='12.7'/%3E%3Ccircle%20cx='60.4'%20cy='81.2'%20r='16.3'/%3E%3Ccircle%20cx='48.3'%20cy='71.5'%20r='17.8'/%3E%3Ccircle%20cx='44.0'%20cy='60.0'%20r='15.7'/%3E%3Ccircle%20cx='48.3'%20cy='48.5'%20r='12.9'/%3E%3Ccircle%20cx='60.4'%20cy='38.8'%20r='12.7'/%3E%3Ccircle%20cx='78.6'%20cy='32.3'%20r='14.6'/%3E%3C/g%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20200%20120'%20preserveAspectRatio='none'%3E%3Cg%20fill='%23000'%3E%3Cellipse%20cx='100'%20cy='60'%20rx='56'%20ry='30'/%3E%3Ccircle%20cx='100.0'%20cy='30.0'%20r='15.9'/%3E%3Ccircle%20cx='121.4'%20cy='32.3'%20r='14.6'/%3E%3Ccircle%20cx='139.6'%20cy='38.8'%20r='12.7'/%3E%3Ccircle%20cx='151.7'%20cy='48.5'%20r='12.9'/%3E%3Ccircle%20cx='156.0'%20cy='60.0'%20r='15.7'/%3E%3Ccircle%20cx='151.7'%20cy='71.5'%20r='17.8'/%3E%3Ccircle%20cx='139.6'%20cy='81.2'%20r='16.3'/%3E%3Ccircle%20cx='121.4'%20cy='87.7'%20r='12.7'/%3E%3Ccircle%20cx='100.0'%20cy='90.0'%20r='10.7'/%3E%3Ccircle%20cx='78.6'%20cy='87.7'%20r='12.7'/%3E%3Ccircle%20cx='60.4'%20cy='81.2'%20r='16.3'/%3E%3Ccircle%20cx='48.3'%20cy='71.5'%20r='17.8'/%3E%3Ccircle%20cx='44.0'%20cy='60.0'%20r='15.7'/%3E%3Ccircle%20cx='48.3'%20cy='48.5'%20r='12.9'/%3E%3Ccircle%20cx='60.4'%20cy='38.8'%20r='12.7'/%3E%3Ccircle%20cx='78.6'%20cy='32.3'%20r='14.6'/%3E%3C/g%3E%3C/svg%3E");
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  filter: drop-shadow(0 10px 16px rgba(150, 130, 165, 0.20));
}
/* the tail: one puff, its smaller partner drawn by its own shadow */
.fs-cue::after {
  content: ''; position: absolute;
  /* Two circles, evenly spaced. The three gaps — cloud to big circle, big to
     small, small to her head — are all the same distance, solved in the script
     and handed here, because they depend on the cloud's own depth and on how
     far above her it is sitting. Setting them by hand made one gap larger than
     the others at every width. (16 Aug 2026, Winnie) */
  left: var(--fs-tail-x, 50%);
  bottom: var(--fs-big-y, -46px);
  width: 28px; height: 28px; margin-left: -14px;
  border-radius: 50%;
  z-index: -1;
  background-color: rgba(255, 255, 255, 0.94);
  /* FOUR thoughts (24 Aug 2026, Winnie): this element is the largest and its
     three shadows are the medium, small and tiny, each stepped one gap further
     down the bend towards her head. Diameters run 17 - 12.5 - 9 - 6.5, a
     steady ~0.73 ladder, so the chain reads as a taper rather than as a row of
     identical dots. A negative spread shrinks a shadow about its own centre,
     so each spread is (diameter - 17) / 2 and the script hands back half the
     difference in the y offset. THESE NUMBERS MIRROR DB/DM/DS/DT in
     founding-story.js — move both files together.
     Every offset is solved in script, because they depend on how deep the
     cloud is and how far above her it sits. */
  /* The fallbacks are NOT decoration: Hydrotherapy has no baby element, so
     placeCue() never runs for it and these defaults are what that milestone
     actually draws. They are the solved geometry for 28/20/14/10 at a 9px gap
     — mid 33, small 59, tiny 80 — so its chain matches every other cloud
     instead of falling back to a different shape. (24 Aug 2026) */
  box-shadow: var(--fs-tp3x, -9px)  var(--fs-mid-y,   33px) 0 -4px rgba(255, 255, 255, 0.93),
              var(--fs-tp2x, -10px) var(--fs-small-y, 59px) 0 -7px rgba(255, 255, 255, 0.92),
              var(--fs-tp1x, 0px)   var(--fs-tiny-y,  80px) 0 -9px rgba(255, 255, 255, 0.91),
              0 6px 12px rgba(150, 130, 165, 0.16);
  pointer-events: none;
  animation: fsTailSway 7.5s ease-in-out infinite;
}
@keyframes fsTailSway {
  0%, 100% { transform: translateX(-2px); }
  50%      { transform: translateX(2px); }
}
@media (prefers-reduced-motion: reduce) {
  .fs-cue::after { animation: none; }
}

.fs-cue-label { display: block; }
.fs-puff {
  position: absolute; border-radius: 50%; pointer-events: none;
  transform: translateX(-50%);
  background: rgba(255,253,251,.95);
  box-shadow: 0 0 10px rgba(226,214,240,.6);
  animation: fsPuff 5.4s ease-in-out infinite;
}
@keyframes fsPuff {
  0%, 100% { transform: translateX(-50%) translateY(0)    scale(1);    opacity: .9; }
  50%      { transform: translateX(-50%) translateY(-4px) scale(1.12); opacity: 1; }
}
.fs-root.is-live .fs-cue { animation: fsCueFloat 7s ease-in-out infinite; }
@keyframes fsCueFloat {
  0%, 100% { transform: translateX(-50%) translateY(0)     scale(1); }
  35%      { transform: translateX(-50%) translateY(-6px)  scale(1.012); }
  70%      { transform: translateX(-50%) translateY(-3px)  scale(.996); }
}
/* the little thoughts rise on their own beat, the way a daydream does */
@keyframes fsPuff {
  0%, 100% { transform: translateY(0)   scale(1);   opacity: .92; }
  50%      { transform: translateY(-3px) scale(1.1); opacity: 1; }
}

.fs-cue { transition: opacity .5s var(--fs-ease), transform .5s var(--fs-ease); }
.fs-cue.is-swap { opacity: 0; transform: translateX(-50%) translateY(-4px) scale(.985); }

/* ---------- the reward line ---------- */
.fs-toast {
  position: absolute; left: 50%; top: 14%; transform: translate(-50%, -50%) scale(.92);
  font-family: 'Quicksand', 'Nunito', var(--font, sans-serif);
  font-weight: 600; font-size: var(--fs-cloud-fs);
  color: #33284A; opacity: 0; pointer-events: none;
  padding: 0; background: none; z-index: 7;
  text-shadow: 0 1px 0 rgba(255,255,255,.9), 0 6px 18px rgba(255,253,250,.85);
  /* Her reward arrives the instant the bottle is empty - an eight-tenths
     fade read as a pause between the feed and her reaction.
     (17 Aug 2026, Winnie) */
  transition: opacity .22s var(--fs-ease), transform .28s var(--fs-ease);
}
.fs-toast.is-on { opacity: 1; transform: translate(-50%, -50%) scale(1); }

/* ==========================================================================
   THE CHAPTER PANEL
   A chapter of the founder's message, on glass, over the nursery. --fs-fit is
   set by the script so a long chapter sets itself down to fit rather than
   growing a scrollbar.
   ========================================================================== */
.fs-chapter {
  --fs-fit: 1;
  position: relative;
  max-width: 668px; width: 100%; max-height: 100%; overflow-y: auto;
  padding: calc(clamp(34px, 4vw, 56px) * var(--fs-fit))
           clamp(28px, 4vw, 60px)
           calc(clamp(28px, 3.4vw, 46px) * var(--fs-fit));
  background: rgba(255,253,251,.93);
  -webkit-backdrop-filter: blur(20px) saturate(1.08);
          backdrop-filter: blur(20px) saturate(1.08);
  border: 1px solid rgba(255,255,255,.92);
  border-radius: 18px;
  box-shadow:
    0 1px 2px rgba(120,100,140,.045),
    0 2px 8px -4px rgba(120,100,140,.10),
    0 30px 70px -34px rgba(120,100,140,.34);
  animation: fsRise .9s var(--fs-ease) both;
}
/* the chapter numeral, watermarked. content: "0" attr() rather than a counter,
   because counters do not increment on a hidden element. */
.fs-chapter::before {
  content: "0" attr(data-chapter);
  position: absolute; top: clamp(20px, 2.4vw, 32px); right: clamp(24px, 3vw, 44px);
  font-family: var(--fs-script); font-weight: 500; font-style: italic;
  font-size: clamp(44px, 5.4vw, 74px); line-height: 1;
  color: rgba(212,120,150,.11); pointer-events: none; user-select: none;
}
.fs-chapter--final::before { content: none; }
@keyframes fsRise { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: none; } }
.fs-chapter[hidden] { display: none; }
.fs-chapter:focus { outline: none; }

.fs-chapter-tag {
  display: inline-flex; align-items: center; gap: 11px;
  margin-bottom: calc(22px * var(--fs-fit, 1));
  font-size: 9.5px; font-weight: 600; letter-spacing: .26em; text-transform: uppercase;
  color: var(--fs-mute);
}
.fs-chapter-tag::before { content: ''; width: 26px; height: 1px; background: rgba(212,120,150,.7); }

/* the quoted voices inside a paragraph take the same round script - and so
   does every other italic in the story, so one voice is never set in two
   different faces (17 Aug 2026, Winnie) */
.fs-p em, .fs-p i, .fs-lead em, .fs-pull em,
.fs-chapter em, .fs-chapter i, .fs-pane em, .fs-pane i,
.fs-intro em, .fs-intro i, .fs-cue em, .fs-cue i {
  /* The quoted voice is set exactly as the sentence around it - same face,
     same size, no slant. The quotation marks are what mark it as speech.
     (17 Aug 2026, Winnie) */
  font-family: inherit; font-style: normal; font-weight: inherit;
  font-size: 1em; letter-spacing: inherit;
  color: inherit;
}
.fs-p {
  font-size: calc(17.5px * var(--fs-fit, 1)); line-height: 1.82; color: #55506A;
  margin: 0 0 calc(17px * var(--fs-fit, 1));
  max-width: 56ch; letter-spacing: .003em;
  text-align: justify; text-align-last: left;
  hyphens: none; -webkit-hyphens: none; word-break: normal; overflow-wrap: normal;
}
/* every paragraph of a chapter is set at one size - Winnie, 17 Aug 2026 */
.fs-chapter > .fs-p:first-of-type { font-size: calc(17.5px * var(--fs-fit, 1)); line-height: 1.82; color: #4A4258; }
.fs-pull {
  margin: calc(26px * var(--fs-fit, 1)) 0; padding: 0 0 0 24px;
  border-left: 3px solid rgba(212,120,150,.72);
  font-family: var(--fs-script); font-style: italic; font-weight: 500;
  font-size: calc(clamp(22px, 2.25vw, 29px) * var(--fs-fit, 1)); line-height: 1.44;
  color: #372C4E; letter-spacing: .002em;
}
.fs-lead {
  font-family: var(--fs-script); font-style: italic; font-weight: 500;
  font-size: calc(clamp(20px, 1.95vw, 25px) * var(--fs-fit, 1)); line-height: 1.5;
  color: var(--fs-ink); margin: 0 0 calc(14px * var(--fs-fit, 1));
}
.fs-continue {
  margin-top: calc(clamp(28px, 3.2vw, 40px) * var(--fs-fit, 1)); padding: 15px 34px;
  border: 1px solid rgba(255,255,255,.9); border-radius: 12px; cursor: pointer;
  /* the brand's three tones, left to right */
  background: linear-gradient(100deg, #F7CBD9 0%, #EBD6E9 36%, #D9DEF1 68%, #CBDCEF 100%);
  color: #2A2438;
  font-family: inherit; font-size: 11px; font-weight: 600;
  letter-spacing: .2em; text-transform: uppercase;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.85), 0 10px 22px -12px rgba(120,100,140,.55);
  transition: transform .35s var(--fs-ease), box-shadow .35s var(--fs-ease), filter .35s var(--fs-ease);
}
.fs-continue:hover {
  transform: translateY(-2px); filter: saturate(1.09) brightness(1.02);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.95), 0 16px 30px -14px rgba(120,100,140,.65);
}
.fs-continue span { margin-left: 5px; opacity: .5; }

/* Fresh & Comfy — a clean fade and a small rise. No sparkle. */
.fs-toast--soft { transform: translate(-50%, -46%) scale(.96); }
.fs-toast--soft.is-on { transform: translate(-50%, -56%) scale(1); }

/* Phones get a bigger changing area and a tighter crop of the room, rather
   than the desktop composition shrunk down until she is a thumbnail. */
@media (max-width: 760px) {
  .fs-slot--baby.is-onmat { width: 76%; height: 40%; bottom: 17%; }
  .fs-slot--baby.is-onmat .fs-contact { width: 72%; }
}

/* ==========================================================================
   THE FIVE MILESTONES — shared staging
   ========================================================================== */
/* Two aligned baby layers. Everything about them is identical except which
   picture is on top, which is what lets a state change hold perfectly still. */
.fs-babyimg {
  position: absolute; inset: 0;
  background-size: contain; background-repeat: no-repeat; background-position: center bottom;
  opacity: 1; transition: opacity .2s var(--fs-ease);
}
.fs-babyimg--b { opacity: 0; }
/* she is lifted into the cot: the carpet fades empty behind her */
/* She is lifted out of the room and into her cot: a slow, even fade with a
   breath of lift, so the carpet is empty before the cot fills. */
.fs-slot--baby.is-tucked {
  opacity: 0; transform: translateX(-50%) translateY(-10px) scale(.985);
  transition: opacity 1.15s var(--fs-ease), transform 1.3s var(--fs-ease);
}

/* Nothing in the room is cut out and hovering: every prop casts a soft ellipse. */
.fs-propshadow {
  position: absolute; left: 50%; bottom: -7%; width: 62%; height: 9%;
  transform: translateX(-50%); border-radius: 50%; pointer-events: none;
  background: radial-gradient(ellipse at center, rgba(140,112,124,.26) 0%, rgba(140,112,124,.09) 54%, rgba(140,112,124,0) 78%);
  filter: blur(5px);
  transition: opacity .35s var(--fs-ease), transform .35s var(--fs-ease);
}
.fs-slot--bottle.is-held .fs-propshadow { opacity: .55; transform: translateX(-50%) translateY(5px) scale(1.12); }
.fs-slot--bottle:focus-visible { outline: 2px solid rgba(212,120,150,.85); outline-offset: 6px; border-radius: 10px; }

/* she lies on a mat: the slot is staged per milestone */
.fs-slot--baby[data-ms="nappy"] { width: 22%; height: 57%; bottom: 12%; z-index: 2; }
.fs-slot--baby[data-ms="tummy"] { width: 30%; height: 34%; bottom: 12%; z-index: 2; }
.fs-slot--baby.is-onmat .fs-contact {
  width: 66%; height: 5.5%; bottom: 9%; filter: blur(9px);
  background: radial-gradient(ellipse at center, rgba(140,112,124,.30) 0%, rgba(140,112,124,.10) 52%, rgba(140,112,124,0) 76%);
}

/* A piece of a layered scene, placed in the room's own coordinates. Centred
   on its anchor point, so the number in the config is where the thing IS. */
.fs-layer-art {
  position: absolute; transform: translate(-50%, -50%) scale(.96);
  background-size: contain; background-repeat: no-repeat; background-position: center;
  pointer-events: none; opacity: 0;
  transition: opacity .9s var(--fs-ease), transform 1s var(--fs-ease);
}
.fs-layer-art.is-in { opacity: 1; transform: translate(-50%, -50%) scale(1); }
/* a piece that waits for the visitor to act */
.fs-layer-art.is-later { opacity: 0; transform: translate(-50%, -50%) scale(.9); }
.fs-layer-art.is-later.is-in { opacity: 1; transform: translate(-50%, -50%) scale(1); }
/* the cot settles in rather than snapping — it is the end of the story */
.fs-layer-art.is-later { transition: opacity 1.4s var(--fs-ease), transform 1.5s var(--fs-ease); }

/* She is IN the water, not on it: everything below the waterline dissolves,
   so the tub's own front wall reads as being in front of her. */
.fs-inwater {
  /* She sits deeper now, so the dissolve moves up her body by the same amount
     — the mask edge has to stay ON the water surface, not on her waist. */
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 44%, rgba(0,0,0,.55) 54%, transparent 64%);
          mask-image: linear-gradient(180deg, #000 0%, #000 44%, rgba(0,0,0,.55) 54%, transparent 64%);
}

/* The caregiver stands behind the tub: everything below its back rim is
   masked away, so she is occluded by the bath instead of standing in it. */
.fs-carer {
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 88%, rgba(0,0,0,.55) 93%, transparent 97%);
          mask-image: linear-gradient(180deg, #000 0%, #000 88%, rgba(0,0,0,.55) 93%, transparent 97%);
}
/* She stands still. Rocking her whole body read as floating — only her hands
   and the foam move now. */
.fs-carer.is-in { animation: none; }

/* The shampoo photograph has a hard rectangular edge along its sleeve. Feather
   it so the arm dissolves into the room instead of ending in a box seam. */
.fs-shampoo {
  -webkit-mask-image: linear-gradient(100deg, #000 0%, #000 62%, rgba(0,0,0,.35) 86%, transparent 100%);
          mask-image: linear-gradient(100deg, #000 0%, #000 62%, rgba(0,0,0,.35) 86%, transparent 100%);
}
/* the caregiver works gently at her hair */
.fs-shampoo.is-in { animation: fsShampoo 3.6s ease-in-out infinite; }
@keyframes fsShampoo {
  0%, 100% { transform: translate(-50%, -50%) rotate(0deg); }
  50%      { transform: translate(-50%, -51%) rotate(-1.6deg); }
}
/* foam gathers where she is being washed */
.fs-foam.is-in { opacity: .9; animation: fsFoam 5.5s ease-in-out infinite; }
@keyframes fsFoam {
  0%, 100% { transform: translate(-50%, -50%) scale(1); opacity: .82; }
  50%      { transform: translate(-50%, -53%) scale(1.05); opacity: .95; }
}

/* ---------- 03 HYDROTHERAPY ----------
   One composed photograph, framed. Never cut up, never background-removed. */
/* The bath used to arrive as a framed card hovering in the middle of the
   room. No frame now: the photograph is feathered into the nursery and sat
   down on the carpet, so the tub simply stands on the floor. */
.fs-scene {
  position: absolute; left: 50%; bottom: 1%; width: 52%;
  transform: translateX(-50%) translate(calc(var(--fs-px, 0px) * 1.4), calc(var(--fs-py, 0px) * 1.4)) scale(.96);
  z-index: 1; overflow: visible; border: 0; box-shadow: none; border-radius: 0;
  opacity: 0; transition: opacity 1.1s var(--fs-ease), transform 1.2s var(--fs-ease);
  -webkit-mask-image: radial-gradient(ellipse 62% 66% at 50% 50%, #000 52%, rgba(0,0,0,.55) 76%, transparent 100%);
          mask-image: radial-gradient(ellipse 62% 66% at 50% 50%, #000 52%, rgba(0,0,0,.55) 76%, transparent 100%);
  filter: drop-shadow(0 26px 30px rgba(150,124,140,.28));
}
.fs-scene.is-in { opacity: 1; transform: translateX(-50%) translate(calc(var(--fs-px, 0px) * 1.4), calc(var(--fs-py, 0px) * 1.4)) scale(1); }
.fs-scene-img {
  position: absolute; inset: 0; background-size: cover; background-position: center;
  transform: scale(1); transition: transform 1.2s var(--fs-ease);
}
/* the frame breathes by one per cent once the duck is in the water */
.fs-play.is-breathing .fs-scene-img { animation: fsBreathe 9s ease-in-out infinite; }
@keyframes fsBreathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.018); } }
.fs-scene-glow {
  position: absolute; inset: 0; pointer-events: none; display: none;
  background: radial-gradient(120% 80% at 50% 0%, rgba(255,250,242,.22) 0%, rgba(255,250,242,0) 62%);
}
.fs-scene .fs-suds { z-index: 3; opacity: 0; }
.fs-play.is-sudsy .fs-scene .fs-suds { opacity: .85; animation: fsBob 6s ease-in-out infinite; }
/* the duck rides the surface */
.fs-slot--bottle.is-bobbing { animation: fsDuck 4.4s ease-in-out infinite; }
@keyframes fsDuck {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50%      { transform: translateY(-2.5%) rotate(2.4deg); }
}

/* ---------- the milestone rail ----------
   Small premium chapters, not game levels. */
.fs-ms::after {
  /* Numbering removed 16 Aug 2026 (Winnie) — the balls read as a nursery,
     not a numbered rail. */
  content: none;
  position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  font-size: 9px; font-weight: 600; letter-spacing: .18em;
  color: var(--fs-blued); opacity: .75; pointer-events: none;
}
/* Every milestone is open from the start. A finished one softens rather than
   greys out, so the rail reads as progress and not as permission. */
.fs-ms.is-done { opacity: .82; }
/* No mark is singled out. Ringing the first one implied you had to begin
   there, which is exactly the opposite of how the hub now works. */
.fs-ms.is-done::after { color: var(--fs-rose); opacity: .85; }
.fs-ms.is-done .fs-ms-label::after { content: " \2661"; color: var(--fs-rose); }
.fs-ms[disabled] { pointer-events: none; }

/* The milestones run in a fixed order now, so a previous/next chooser has
   nothing to choose. The dots stay as a reading of how far you have come. */
.fs-ck { display: none; }
.fs-ck-row { justify-content: center; }

/* the console card shows what you are about to do, not the brand again */
.fs-ck-brand { color: #33284A; opacity: .78; display: block; line-height: 0; }
.fs-ck-brand svg { display: block; margin: 0 auto; }

@media (prefers-reduced-motion: reduce) {
  .fs-scene, .fs-scene-img { transition: opacity .3s linear; animation: none !important; }
  .fs-slot--bottle.is-bobbing { animation: none; }
  .fs-babyimg { transition: opacity .3s linear; }
  .fs-slot--baby.is-wriggling { animation: none; }
  .fs-ripple { animation: none; opacity: 0; }
  .fs-play.is-sudsy .fs-suds,
  .fs-moon, .fs-star, .fs-slot--bottle.is-shaking { animation: none; }
  .fs-heart.is-up { animation: none; opacity: 0; }
  .fs-slot--baby.is-settling,
  .fs-shampoo.is-in, .fs-foam.is-in, .fs-carer.is-in,
  .fs-ms-face,
  .fs-root.is-live .fs-cue, .fs-puff,
  .fs-slot--bottle.fs-slot--soft.is-hot { animation: none; }
  .fs-mat { transition: opacity .4s linear; transform: translateX(-50%); }
  .fs-mat.is-in { transform: translateX(-50%); }
}

/* ==========================================================================
   THE CURTAIN AS A TITLE PAGE
   The founders' block is moved into the intro layer at runtime, so the closed
   curtain carries the portrait, the heading and both signatures — and the
   nursery is revealed behind it. Scoped to .fs-onstage so the block's normal
   styling is untouched if it is ever left in place.
   ========================================================================== */
.fs-intro { width: 100%; max-width: 1120px; margin-left: auto; margin-right: auto; align-self: center; }
.fs-intro .fs-onstage {
  /* auto columns + justify-content:center makes the pair hug its content and
     sit on the same centre line as the lines beneath it. A 1fr second column
     eats the leftover width and drags everything left. */
  display: grid; grid-template-columns: auto minmax(0, 520px);
  justify-content: center; gap: clamp(26px, 3.2vw, 52px);
  align-items: center; text-align: left; width: 100%;
  margin: 0 0 clamp(18px, 2.4vw, 30px); padding: 0; border: 0;
}
.fs-intro .fs-onstage .about-visual { width: clamp(270px, 31vw, 424px); margin-top: clamp(10px, 1.5vw, 24px); }
.fs-intro .fs-onstage .founders-sig-block { gap: clamp(14px, 2vw, 26px); }
.fs-intro .fs-onstage .reveal { opacity: 1 !important; transform: none !important; }
.fs-intro .fs-onstage .about-visual {
  aspect-ratio: 4 / 5; border-radius: 6px; overflow: hidden;
  box-shadow: 0 18px 36px -20px rgba(150, 124, 160, .5);
}
.fs-intro .fs-onstage .about-visual img { width: 100%; height: 100%; object-fit: cover; }
.fs-intro .fs-onstage .about-content h3 {
  font-size: clamp(23px, 2.9vw, 38px); line-height: 1.14; margin: 12px 0 5px;
}
/* This heading reads in the ink of the message beneath it rather than the
   site's blue. Scoped to the founders' block, so no other page changes.
   (17 Aug 2026, Winnie) */
.founders .about-content h3 em,
.fs-intro .fs-onstage .about-content h3 em { color: var(--fs-ink, #2A2438); }
.fs-intro .fs-onstage .about-content .eyebrow { margin-bottom: 6px; }
/* "Step into our nursery below" is redundant once you are standing in it */
.fs-intro .fs-onstage .about-content .body-text:not([style]) { display: none; }
.fs-intro .fs-onstage .founders-sig { margin-top: 14px; padding-top: 12px; }
.fs-intro .fs-onstage .founder-name { font-size: clamp(20px, 2.3vw, 28px); }
.fs-intro .fs-onstage .family-chase-v2-strip { display: none; }

@media (max-width: 900px) {
  .fs-intro .fs-onstage { grid-template-columns: minmax(0, 1fr); gap: 12px; text-align: center; }
  .fs-intro .fs-onstage .about-visual { width: clamp(230px, 66vw, 320px); max-width: none; margin: 10px auto 0; }
  .fs-intro .fs-onstage .founders-sig { justify-content: center; }
  .fs-intro .fs-lead { font-size: 15px; }
  .fs-intro .fs-pull { font-size: 19px; }
}

/* The Baby Angels mark, in mono, in the corner of the title page — where a
   printed book puts its imprint, out of the way of the portrait. */
.fs-mark {
  /* the title page's own corner mark */
  position: absolute; top: clamp(26px, 2.8vw, 44px); right: clamp(26px, 2.9vw, 48px);
  display: block; width: clamp(52px, 5.4vw, 78px); height: auto;
  margin: 0; z-index: 6;
  /* it belongs to the title page, so it fades with the curtain */
  opacity: 0;
  transition: opacity .8s var(--fs-ease);
  pointer-events: none;
  filter: drop-shadow(0 1px 2px rgba(255, 253, 250, .9));
}
@media (max-width: 720px) {
  /* Smaller and tucked into the frame's own corner, so it keeps clear air
     between itself and the portrait on a stacked layout. */
  /* the same gap on both sides of the corner */
  .fs-mark { width: clamp(34px, 8vw, 44px); top: 26px; right: 26px; }
}

/* ---------- no-JS fallback ----------
   The whole message, plainly, always in the DOM so it is readable with
   JavaScript off and by anything that cannot run the nursery. Hidden the
   moment the interactive version is live — losing this rule is what put the
   entire message back on the page below the frame. */
.fs-fallback { max-width: 720px; margin: clamp(28px, 4vw, 48px) auto 0; padding: 0 20px; }
.fs-root.is-live .fs-fallback { display: none; }

/* ==========================================================================
   CLOSING THE GAP TO THE FOUNDERS BLOCK
   Two things were opening a dead band above this section on About Us: the
   about-grid's own bottom margin, and the family-chase strip, whose sprite
   (Image/family_chase_v2.png) does not exist — so it reserves ~190px and
   paints nothing. Both are neutralised here rather than in styles.css, so the
   rest of the site is untouched. Restore the strip by deleting the rule below
   once the sprite is supplied.
   ========================================================================== */
.founders .family-chase-v2-strip { display: none; }
.founders .about-grid { margin-bottom: clamp(26px, 3vw, 44px); }
.founders { padding-bottom: 0; }

/* ==========================================================================
   MILESTONE STAGING — restored
   These rules were lost in an earlier edit, which is why the changing mat had
   no size or position, the bedtime sky never appeared, the feeding heart was
   invisible and the instruction never faded out.
   ========================================================================== */

/* the mat a milestone lays down */
.fs-mat {
  --mx: calc(var(--fs-px, 0px) * 1.5);
  --my: calc(var(--fs-py, 0px) * 1.5);
  --mrot: 0deg;
  position: absolute; z-index: 0; pointer-events: none; opacity: 0;
  background-size: contain; background-repeat: no-repeat; background-position: center center;
  transform: translateX(-50%) translate(var(--mx), var(--my)) rotate(var(--mrot)) scale(.94);
  transition: opacity .9s var(--fs-ease), transform 1s var(--fs-ease);
}
.fs-mat.is-in {
  opacity: 1;
  transform: translateX(-50%) translate(var(--mx), var(--my)) rotate(var(--mrot)) scale(1);
}
/* drawn fallback, used only while a mat image is missing */
.fs-mat--drawn {
  --mrot: -20deg;
  border-radius: 46% / 40%; opacity: .94;
  background:
    radial-gradient(120% 150% at 42% 14%, rgba(255,252,248,.80) 0%, rgba(252,240,236,.42) 46%, rgba(240,222,220,0) 78%),
    linear-gradient(168deg, #FAF0EA 0%, #F3E2DE 46%, #E7D2CF 100%);
  box-shadow:
    inset 0 6px 18px rgba(255,255,255,.85),
    inset 0 -14px 32px rgba(184,150,158,.26),
    0 22px 40px -18px rgba(140,112,128,.52);
}
.fs-mat--drawn.is-in { opacity: .94; }

/* the care itself is implied: a warm glow, held for a moment and gone */
.fs-privacy {
  position: absolute; aspect-ratio: 2 / 1; transform: translateX(-50%);
  border-radius: 50%; pointer-events: none; z-index: 5;
  opacity: 0; transition: opacity .75s var(--fs-ease);
  background: radial-gradient(ellipse at center,
     rgba(255,251,244,.95) 0%, rgba(255,247,238,.58) 40%, rgba(255,245,234,0) 72%);
  filter: blur(7px);
}
.fs-privacy.is-on { opacity: 1; }

/* bedtime: the sky comes out only once dusk falls */
.fs-sky {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  opacity: 0; transition: opacity 2.2s var(--fs-ease);
}
.fs-play.is-night .fs-sky { opacity: 1; }

/* dusk over the whole room — an overlay, never a second nursery */
.fs-root.is-dusk .fs-bg,
.fs-root.is-dusk .fs-room { filter: brightness(.86) saturate(.94); transition: filter 2s var(--fs-ease); }
/* the cot patch is a piece of that same room, so it takes the same evening */
.fs-root.is-dusk .fs-layer-art { filter: brightness(.86) saturate(.94); transition: filter 2s var(--fs-ease); }
.fs-root.is-dusk .fs-vignette {
  background:
    radial-gradient(115% 85% at 50% 40%, rgba(74,66,110,0) 38%, rgba(74,66,110,.30) 100%),
    linear-gradient(180deg, rgba(120,110,170,.16) 0%, rgba(90,84,140,.24) 100%);
  transition: background 2s var(--fs-ease);
}

/* feeding: a pearl heart lifts away once she is full */
.fs-heart {
  position: absolute; aspect-ratio: 420 / 381; z-index: 6; pointer-events: none;
  background-size: contain; background-repeat: no-repeat;
  transform: translate(-50%, -50%) scale(.4); opacity: 0;
  filter: drop-shadow(0 8px 20px rgba(214,160,180,.4));
}
.fs-heart.is-up { animation: fsHeart 2.8s ease-out forwards; }
@keyframes fsHeart {
  0%   { opacity: 0;   transform: translate(-50%, -50%)  scale(.4); }
  22%  { opacity: .95; transform: translate(-50%, -95%)  scale(1); }
  70%  { opacity: .85; transform: translate(-50%, -180%) scale(1.04); }
  100% { opacity: 0;   transform: translate(-50%, -250%) scale(1.1); }
}

/* the instruction steps aside once the prop is picked up */
.fs-cue.is-gone { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .fs-heart.is-up { animation: none; opacity: 0; }
  .fs-privacy, .fs-sky { transition: opacity .3s linear; }
}

/* ==========================================================================
   PHONES — last word
   These overrides live at the END of the file on purpose. The milestone rail
   and the console were written later than the original mobile block, so on a
   phone the desktop layout was winning: the marks ran off both edges of the
   frame and the console card covered them.
   ========================================================================== */
@media (max-width: 700px) {
  /* A 3:2 frame on a phone is only ~240px tall — too short to hold a scene
     and a rail. Taller crops the room's sides, and every prop is placed in
     room coordinates, so nothing drifts. */
  .fs-stage { aspect-ratio: 5 / 6; }

  .fs-ms-wrap {
    position: absolute; inset: auto 0 auto 0; left: 0; top: 3%;
    transform: none; width: 100%; padding: 0 8px;
    display: flex; flex-wrap: wrap; justify-content: center;
    gap: 4px 2px; perspective: none;
  }
  .fs-ms {
    position: relative; flex: 0 0 auto; width: 19%; min-width: 0;
    padding-top: 13px;
  }
  /* These margins belonged to the old flex rail. The marks are placed by
     script now, in room coordinates, and a margin on top of that placement
     pushed each one down by a fixed number of pixels — 16, 0, 22, 5 and 19 —
     which is why a ball would sit on its perch at one screen size and float
     above it at another. Nothing may move a mark off the point it is given. */
  .fs-ms:nth-child(1),
  .fs-ms:nth-child(2),
  .fs-ms:nth-child(3),
  .fs-ms:nth-child(4),
  .fs-ms:nth-child(5) { margin-top: 0 !important; }
  .fs-ms:nth-child(1) .fs-ms-face { width: 48px; height: 48px; }
  .fs-ms:nth-child(2) .fs-ms-face { width: 39px; height: 39px; }
  .fs-ms:nth-child(3) .fs-ms-face { width: 55px; height: 55px; }
  .fs-ms:nth-child(4) .fs-ms-face { width: 42px; height: 42px; }
  .fs-ms:nth-child(5) .fs-ms-face { width: 50px; height: 50px; }
  /* The float is what makes them read as glass rather than stickers, so it
     stays on a phone — just a shorter rise. */
  .fs-ms-face { width: 46px; height: 46px; margin-bottom: 5px;
                animation: fsMarkFloatSm 6.5s ease-in-out infinite; }
  .fs-ms-face svg { width: 21px; height: 21px; }
  .fs-ms-label { font-size: 7.5px; letter-spacing: .08em; }
  .fs-ms::after { font-size: 7.5px; }

  /* the console becomes a slim bar along the bottom, never a card over the room */
  .fs-console {
    left: 50%; right: auto; bottom: 8px; transform: translateX(-50%);
    width: auto; max-width: calc(100% - 20px);
    display: flex; align-items: center; gap: 10px;
    padding: 7px 8px 7px 14px; border-radius: 100px;
  }
  .fs-ck-brand { display: none; }
  .fs-ck-name { font-size: 11px; margin: 0; white-space: nowrap; flex: 0 1 auto; }
  .fs-ck-row { margin: 0; }
  .fs-ck-dots { display: none; }
  .fs-ck-begin { margin: 0; padding: 9px 16px; font-size: 9.5px; border-radius: 100px;
    flex: 0 0 auto; white-space: nowrap; width: auto; }

  /* the play area gets the room below the rail */
  /* Brought down the frame so she sits close to the glass, with the bottle
     and her thought cloud following her - both are placed from her own box,
     so the group keeps its proportions. (17 Aug 2026, Winnie) */
  .fs-slot--baby            { bottom: 5%; }
  .fs-slot--baby[data-ms="nappy"] { width: 40%; height: 56%; bottom: 9%; }
  .fs-slot--baby[data-ms="tummy"] { width: 52%; height: 34%; bottom: 7%; }
  .fs-root { --fs-cloud-fs: 13px; }
  .fs-cue { top: 25%; padding: 16px 20px 18px; }
  .fs-chapter { padding: 20px 18px; }
}

/* ==========================================================================
   THE TITLE PAGE ON SMALL SCREENS — final word
   styles.css sets .about-visual to width:100% / max-width:480px with
   !important inside its own media query, so the portrait filled the whole
   frame here and pushed the heading, the opening lines and the Begin button
   out of the stage entirely. Matching !important is the only way to win it,
   and it is scoped to .fs-onstage so the About page's own layout is untouched.
   ========================================================================== */
@media (max-width: 900px) {
  .fs-intro .fs-onstage .about-visual {
    width: clamp(212px, 60vw, 300px) !important;
    max-width: none !important;
    /* A clear breath under the portrait before the heading, and — 24 Aug 2026,
       Winnie: "leave some space from the picture to the curtain window at the
       top side" — a real gap ABOVE it too, so the photograph sits inside the
       curtain rather than starting at its rail. `auto` on both sides keeps it
       centred in the frame. Phones only; the desktop title page is untouched. */
    margin: clamp(34px, 9vw, 58px) auto 0 !important;
    aspect-ratio: 4 / 5 !important;
    height: auto !important;
  }
  .fs-intro .fs-onstage { gap: 10px; margin-bottom: 12px; }
  .fs-intro .fs-onstage .about-content h3 { font-size: clamp(18px, 4.6vw, 25px); margin: 8px 0 4px; }
  .fs-intro .fs-onstage .founder-name { font-size: clamp(15px, 3.6vw, 19px); }
  .fs-intro .fs-onstage .founders-sig { margin-top: 8px; padding-top: 8px; }
  .fs-intro .fs-lead { font-size: 13.5px; }
  .fs-intro .fs-pull { font-size: 16px; margin: 10px 0; }
  .fs-intro-line { margin-bottom: 6px; }
  .fs-intro .fs-btn { margin-top: 10px; padding: 11px 22px; font-size: 10px; }
  .fs-layer { padding: 12px; }
}

/* ==========================================================================
   THE CHAPTER AS TWO FLOATING PANES
   The founder's message used to arrive as one tall card sitting in the middle
   of the nursery. Split at an existing paragraph break, the same words become
   two panes of frosted glass — one upper left, one lower right — with the room
   visible between and behind them, in the language of the landing page.
   Applies to every chapter and the closing panel alike.
   ========================================================================== */
.fs-chapter {
  position: absolute; inset: 0;
  width: auto; max-width: none; max-height: none; overflow: visible;
  background: none; border: 0; box-shadow: none;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  padding: 0;
}
.fs-chapter::before {          /* the numeral moves to the frame's own corner */
  top: clamp(10px, 1.6vw, 22px); right: clamp(14px, 2vw, 28px);
  color: rgba(212,120,150,.16);
}

.fs-pane {
  position: absolute;
  width: min(38%, 412px);
  padding: 0;
  animation: fsPaneIn .95s var(--fs-ease) both;
}
/* No box. The landing page sets its words straight on the field with nothing
   drawn round them, and that is what reads as modern here too: a breath of
   light lifted off the room, edgeless, so the nursery simply grows quieter
   where the text sits. Nothing to align to, nothing to crop the words. */
.fs-pane::before {
  content: ''; position: absolute; z-index: 0; pointer-events: none;
  inset: -13% -13% -12%;
  background: rgba(255,253,251,.93);
  -webkit-backdrop-filter: blur(18px) saturate(1.06);
          backdrop-filter: blur(18px) saturate(1.06);
  /* Flat where the words are, feathered only past them — a radial falls off
     too soon and the last line of a long chapter loses its ground. Two
     crossed gradients intersect to give a rectangle with no edge. */
  -webkit-mask-image:
    linear-gradient(to right,  transparent 0%, #000 9%, #000 91%, transparent 100%),
    linear-gradient(to bottom, transparent 0%, #000 8%, #000 92%, transparent 100%);
  -webkit-mask-composite: source-in;
          mask-image:
    linear-gradient(to right,  transparent 0%, #000 9%, #000 91%, transparent 100%),
    linear-gradient(to bottom, transparent 0%, #000 8%, #000 92%, transparent 100%);
          mask-composite: intersect;
}
.fs-pane > * { position: relative; z-index: 1; }

/* The chapter opens exactly the way a landing-page section does: a hairline
   rule, the label in small caps, then the words. */
.fs-pane .fs-chapter-tag {
  gap: calc(14px * var(--fs-fit, 1));
  margin-bottom: calc(20px * var(--fs-fit, 1));
  font-size: calc(10px * var(--fs-fit, 1)); letter-spacing: .28em;
  color: #9C93AE;
}
.fs-pane .fs-chapter-tag::before {
  display: block; width: calc(34px * var(--fs-fit, 1)); height: 1px;
  background: rgba(212,120,150,.6);
}
/* one size for every paragraph, opening line included - Winnie, 17 Aug 2026 */
.fs-pane--a > .fs-p:first-of-type {
  font-size: calc(17.5px * var(--fs-fit, 1)); line-height: 1.82; color: #443C54;
}
.fs-pane .fs-p { max-width: none; }

/* Two columns of light, one lifted and one dropped, set into the room's own
   empty space rather than centred over its furniture. */
/* One grid, one gutter: equal air on both flanks, the columns set in from the
   frame rather than pushed against it. The vertical placement is finished in
   script, which centres the pair on the frame once it knows how tall it is. */
.fs-pane--a { right: calc(50% + 2.6%); top: 7%; }
.fs-pane--b { left:  calc(50% + 2.6%); top: 24%; animation-delay: .22s; }
@keyframes fsPaneIn {
  from { opacity: 0; transform: translateY(22px) scale(.985); }
  to   { opacity: 1; transform: none; }
}
/* each pane drifts on its own beat, so the pair never reads as one block */
.fs-root.is-live .fs-pane--a { animation: fsPaneIn .95s var(--fs-ease) both, fsPaneDrift 13s ease-in-out 1s infinite; }
.fs-root.is-live .fs-pane--b { animation: fsPaneIn .95s var(--fs-ease) .22s both, fsPaneDrift 15s ease-in-out 2.2s infinite; }
@keyframes fsPaneDrift {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-7px); }
}
.fs-pane .fs-p:last-of-type { margin-bottom: 0; }
.fs-pane .fs-continue { margin-top: clamp(16px, 1.8vw, 26px); }

/* Phones cannot hold two panes side by side, so they stack and the frame
   grows to suit — the same behaviour a long chapter already had. */
@media (max-width: 760px) {
  /* The chapter fills the window on a phone too, so its veil can be inset by
     the same amount on all four sides. It used to sit in normal flow and take
     only the height of its words, which is why the white shrank and grew with
     the chapter. (17 Aug 2026, Winnie) */
  .fs-chapter { position: absolute; inset: 0; box-sizing: border-box; }
  .fs-pane {
    position: relative; left: auto; right: auto; top: auto; bottom: auto;
    width: 100%; margin: 0 0 26px;
  }
  /* One veil for the whole chapter, inset the SAME amount on all four sides,
     so the white reads as a frame set into the window rather than a shape
     that grows and shrinks with the length of the words. The per-column veil
     is switched off here. (17 Aug 2026, Winnie) */
  .fs-pane::before { content: none; }
  /* 24 Aug 2026, Winnie: "top left right bottom side same side leaving some
     space to the screen". 14px was an even inset but too tight to read as a
     panel — the white ran almost to the frame's edge on every side. It now
     stands off by roughly 5% of the frame's width, still the SAME on all four
     sides, so the nursery shows as a clear border around the card. */
  .fs-chapter::after {
    content: ''; position: absolute; inset: clamp(18px, 5vw, 30px); z-index: 0;
    border-radius: 20px;
    background: rgba(255,253,251,.90);
    -webkit-backdrop-filter: blur(16px) saturate(1.06);
            backdrop-filter: blur(16px) saturate(1.06);
    box-shadow: 0 18px 44px -26px rgba(120,100,140,.55);
    pointer-events: none;
    /* The veil fades out on all four edges by the same number of pixels, so
       the white dissolves into the nursery instead of ending on a line. Set in
       px, not per cent: 3% of a tall frame is twice 3% of a narrow one, and
       the top and bottom feathered differently from the sides.
       (17 Aug 2026, Winnie) */
    -webkit-mask-image:
      linear-gradient(to right,  transparent 0, #000 20px, #000 calc(100% - 20px), transparent 100%),
      linear-gradient(to bottom, transparent 0, #000 20px, #000 calc(100% - 20px), transparent 100%);
            mask-image:
      linear-gradient(to right,  transparent 0, #000 20px, #000 calc(100% - 20px), transparent 100%),
      linear-gradient(to bottom, transparent 0, #000 20px, #000 calc(100% - 20px), transparent 100%);
    -webkit-mask-composite: source-in; mask-composite: intersect;
  }
  .fs-pane { position: relative; z-index: 1; }
  .fs-pane--b { margin-bottom: 0; }
  .fs-root.is-live .fs-pane--a,
  .fs-root.is-live .fs-pane--b { animation: fsPaneIn .8s var(--fs-ease) both; }
}
@media (prefers-reduced-motion: reduce) {
  .fs-pane, .fs-root.is-live .fs-pane--a, .fs-root.is-live .fs-pane--b { animation: none; }
}


/* ---------------------------------------------------------------------------
   NO FLASH OF THE UNMOVED FOUNDERS' BLOCK
   founding-story.js moves `.founders .about-grid` onto the curtain when the
   section scrolls into view. Before that it laid out in the page at full
   width — the portrait appeared standalone, then jumped into the frame. Held
   back until it has been moved. Scoped to html.fs-js, which only exists while
   the story script is running, so the no-JS fallback still shows everything.
   --------------------------------------------------------------------------- */
html.fs-js .founders .about-grid:not(.fs-onstage) { display: none; }

/* The signatures were competing with the founders' names in the artwork above
   them; drawn smaller they read as a signature rather than a heading. */
.fs-intro .fs-onstage .founder-name {
  font-size: clamp(27px, 2.9vw, 38px) !important;
  line-height: 1.15 !important;
  margin-bottom: 0 !important;
}
/* the names and their one label read as a single signed block — every scrap
   of inherited spacing between them goes */
.fs-intro .fs-onstage .founders-sig-block { margin-bottom: 0 !important; padding-bottom: 0 !important; }
.fs-intro .fs-onstage .founder-signature { margin-bottom: 0 !important; padding-bottom: 0 !important; gap: 0 !important; }
.fs-intro .fs-onstage .founders-sig-divider { align-self: center; }

/* The eyebrow is redundant: the heading directly beneath already says whose
   message this is. (Winnie, 15 Aug 2026) */
.fs-intro .fs-onstage .about-content .eyebrow { display: none !important; }
.fs-intro .fs-onstage .founder-role {
  font-size: 11.5px !important; letter-spacing: .2em !important;
  font-weight: 500 !important; opacity: .72;
}
@media (max-width: 900px) {
  .fs-intro .fs-onstage .founder-name { font-size: clamp(18px, 4.05vw, 22.5px) !important; }
  .fs-intro .fs-onstage .founder-role { font-size: 9.4px !important; letter-spacing: .16em !important; }
}


/* ---------------------------------------------------------------------------
   THE SIGNATURES — centred, with one label for the pair
   Two "CO-FOUNDER" captions under two names read as a repeated field label.
   The pair now signs together under a single line, centred on the column.
   --------------------------------------------------------------------------- */
.fs-intro .fs-onstage .founders-sig {
  display: flex; flex-direction: column; align-items: center;
  width: 100%; text-align: center;
}
.fs-intro .fs-onstage .founders-sig-rule { margin: 0 auto; }
.fs-intro .fs-onstage .founders-sig-block {
  justify-content: center; align-items: flex-end; width: auto;
}
.fs-intro .fs-onstage .founder-signature { align-items: center; text-align: center; }
.founders-sig-role {
  display: block;
  /* Pulled up under the names — the script face carries a deep descender
     space, so a zero margin still left a visible band. The same amount is
     handed back below, so nothing after it moves. */
  margin-top: calc(-15px * var(--fs-fit, 1));
  margin-bottom: calc(15px * var(--fs-fit, 1));
  font-family: var(--font, 'Inter', sans-serif);
  font-size: 11.5px; font-weight: 500; letter-spacing: .2em;
  text-transform: uppercase; color: var(--fs-mute, #9C93AE); opacity: .78;
  text-align: center;
}
@media (max-width: 900px) {
  .founders-sig-role { font-size: 9.4px; letter-spacing: .16em; margin-top: -14px; margin-bottom: 14px; }
}


/* ---------------------------------------------------------------------------
   CHAPTERS ON A PHONE — smaller type, and no justification
   Justified text in a 300px column opens rivers between the words, which is
   what made the lines look stretched. Set flush left at a smaller size, a
   whole chapter now sits in the frame without scrolling.
   --------------------------------------------------------------------------- */
@media (max-width: 760px) {
  .fs-pane .fs-p {
    font-size: calc(14.2px * var(--fs-fit, 1)) !important;
    line-height: 1.68 !important;
    margin-bottom: calc(12px * var(--fs-fit, 1)) !important;
    text-align: justify !important;
    text-align-last: left !important;
    max-width: none !important;
  }
  .fs-pane--a > .fs-p:first-of-type {
    font-size: calc(14.2px * var(--fs-fit, 1)) !important;
    line-height: 1.68 !important;
  }
  .fs-pane .fs-lead { font-size: calc(16.5px * var(--fs-fit, 1)) !important; line-height: 1.44 !important; }
  .fs-pane .fs-pull { font-size: calc(17.5px * var(--fs-fit, 1)) !important; line-height: 1.38 !important; }
  .fs-pane .fs-chapter-tag { font-size: calc(9px * var(--fs-fit, 1)) !important; margin-bottom: calc(14px * var(--fs-fit, 1)) !important; }
  .fs-pane { margin-bottom: 18px; }
}


/* ---------------------------------------------------------------------------
   CHANGING ON A PHONE
   The instruction sits centred over the scene with clear air above her head,
   rather than hanging off her crown where the frame is tightest.
   --------------------------------------------------------------------------- */
/* This block used to pin the cue with `top: 4% !important`, which also beat the
   inline position the script writes — so under 760px the thought cloud ignored
   the baby and sat by the curtain rail. Only the wrapping belongs to the phone.
   (16 Aug 2026, Winnie) */
@media (max-width: 760px) {
  .fs-cue {
    white-space: normal;
    /* Measured in characters, not per cent of the frame. As a percentage the
       same sentence broke onto two lines on one screen and three on another, and
       a three-line block makes a taller, lumpier cloud — which is why the
       milestones stopped matching each other. In ch it breaks identically at
       every width. (16 Aug 2026, Winnie) */
    /* Shrink-to-fit on an absolutely positioned box is capped by the space
       between its left edge and the frame, which is what kept the cloud narrow
       and made every sentence run to four lines however wide the max-width was.
       `max-content` sizes it to the sentence first, and the max-width then
       decides where it wraps. (17 Aug 2026, Winnie) */
    width: max-content;
    max-width: min(44ch, 86%);
    text-align: center;
  }
  /* she moves down the mat so the instruction is not sitting on her head */
  /* The changing group rides up together: the cushion is measured from her
     box and her cloud from her head, so one number moves all three and the
     clean nappy - which is placed against the frame - stays where it is.
     (17 Aug 2026, Winnie) */
  .fs-slot--baby[data-ms="nappy"] { bottom: 12%; }
}


/* ---------------------------------------------------------------------------
   HER THOUGHT, AS A CLOUD
   The reward line was set as bare words over the room. It is her thought, so
   it now sits in a cloud with three little thoughts curling up to it from her
   head — the trail is placed on a bend in script, never a straight run.
   --------------------------------------------------------------------------- */
.fs-toast {
  padding: clamp(14px, 1.6vw, 22px) clamp(24px, 2.6vw, 38px) clamp(16px, 1.8vw, 24px) !important;
  /* The cloud is painted on a layer BEHIND the words, not on the element
     itself. Masking the element meant the cloud only covered the middle ~74%
     of its own box — the lobes never reach the edges — so a long reward line
     ran straight past the painted edge and lost its first and last letters.
     The layer is inset negatively, exactly as the thought clouds are, so the
     words always sit well inside the cloud. (16 Aug 2026, Winnie) */
  background: none !important;
  -webkit-mask-image: none !important; mask-image: none !important;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  filter: none;
  color: #3B3350 !important;
  text-shadow: none !important;
  white-space: nowrap;
  z-index: 8;
}
.fs-toast::before {
  content: ''; position: absolute;
  /* Wide on purpose. The mask paints only the middle ~74% of this layer's
     width and ~68% of its height, so the layer has to overhang the words by a
     good margin or the cloud's edge crops them. Solved for ~18px of clear
     cloud either side of the longest reward line. (16 Aug 2026, Winnie) */
  inset: -1.15em -4.2em -1.1em;
  z-index: -1;
  pointer-events: none;
  background-image:
    linear-gradient(180deg, rgba(255,255,255,.72) 0%, rgba(255,255,255,.86) 42%, rgba(255,255,255,.70) 100%),
    url('Image/dreamy-cloud-web.jpg');
  background-size: cover, cover;
  background-position: center, center;
  background-repeat: no-repeat, no-repeat;
  -webkit-mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20200%20120'%20preserveAspectRatio='none'%3E%3Cg%20fill='%23000'%3E%3Cellipse%20cx='100'%20cy='60'%20rx='56'%20ry='30'/%3E%3Ccircle%20cx='100.0'%20cy='30.0'%20r='15.9'/%3E%3Ccircle%20cx='121.4'%20cy='32.3'%20r='14.6'/%3E%3Ccircle%20cx='139.6'%20cy='38.8'%20r='12.7'/%3E%3Ccircle%20cx='151.7'%20cy='48.5'%20r='12.9'/%3E%3Ccircle%20cx='156.0'%20cy='60.0'%20r='15.7'/%3E%3Ccircle%20cx='151.7'%20cy='71.5'%20r='17.8'/%3E%3Ccircle%20cx='139.6'%20cy='81.2'%20r='16.3'/%3E%3Ccircle%20cx='121.4'%20cy='87.7'%20r='12.7'/%3E%3Ccircle%20cx='100.0'%20cy='90.0'%20r='10.7'/%3E%3Ccircle%20cx='78.6'%20cy='87.7'%20r='12.7'/%3E%3Ccircle%20cx='60.4'%20cy='81.2'%20r='16.3'/%3E%3Ccircle%20cx='48.3'%20cy='71.5'%20r='17.8'/%3E%3Ccircle%20cx='44.0'%20cy='60.0'%20r='15.7'/%3E%3Ccircle%20cx='48.3'%20cy='48.5'%20r='12.9'/%3E%3Ccircle%20cx='60.4'%20cy='38.8'%20r='12.7'/%3E%3Ccircle%20cx='78.6'%20cy='32.3'%20r='14.6'/%3E%3C/g%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20200%20120'%20preserveAspectRatio='none'%3E%3Cg%20fill='%23000'%3E%3Cellipse%20cx='100'%20cy='60'%20rx='56'%20ry='30'/%3E%3Ccircle%20cx='100.0'%20cy='30.0'%20r='15.9'/%3E%3Ccircle%20cx='121.4'%20cy='32.3'%20r='14.6'/%3E%3Ccircle%20cx='139.6'%20cy='38.8'%20r='12.7'/%3E%3Ccircle%20cx='151.7'%20cy='48.5'%20r='12.9'/%3E%3Ccircle%20cx='156.0'%20cy='60.0'%20r='15.7'/%3E%3Ccircle%20cx='151.7'%20cy='71.5'%20r='17.8'/%3E%3Ccircle%20cx='139.6'%20cy='81.2'%20r='16.3'/%3E%3Ccircle%20cx='121.4'%20cy='87.7'%20r='12.7'/%3E%3Ccircle%20cx='100.0'%20cy='90.0'%20r='10.7'/%3E%3Ccircle%20cx='78.6'%20cy='87.7'%20r='12.7'/%3E%3Ccircle%20cx='60.4'%20cy='81.2'%20r='16.3'/%3E%3Ccircle%20cx='48.3'%20cy='71.5'%20r='17.8'/%3E%3Ccircle%20cx='44.0'%20cy='60.0'%20r='15.7'/%3E%3Ccircle%20cx='48.3'%20cy='48.5'%20r='12.9'/%3E%3Ccircle%20cx='60.4'%20cy='38.8'%20r='12.7'/%3E%3Ccircle%20cx='78.6'%20cy='32.3'%20r='14.6'/%3E%3C/g%3E%3C/svg%3E");
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  filter: drop-shadow(0 10px 16px rgba(150,130,165,.20));
}
/* the three thoughts on their way up */
.fs-tpuff {
  position: absolute;
  border-radius: 50%;
  background: rgba(255,255,255,.40);
  -webkit-backdrop-filter: blur(9px) saturate(1.08) brightness(1.07);
          backdrop-filter: blur(9px) saturate(1.08) brightness(1.07);
  box-shadow: inset 0 0 8px rgba(255,255,255,.6);
  transform: translate(-50%, -50%) scale(.4);
  opacity: 0;
  pointer-events: none;
  z-index: 8;
  transition: opacity .45s var(--fs-ease), transform .55s var(--fs-ease);
}
/* A size up for a desktop frame (24 Aug 2026, Winnie). The spacing between
   them is solved in founding-story.js from these very widths, so they cannot
   collide however short the climb to the cloud is. */
.fs-tpuff--1 { width: clamp(9px, 1.05vw, 14px);  height: clamp(9px, 1.05vw, 14px);  }
.fs-tpuff--2 { width: clamp(13px, 1.55vw, 20px); height: clamp(13px, 1.55vw, 20px); }
.fs-tpuff--3 { width: clamp(18px, 2.15vw, 28px); height: clamp(18px, 2.15vw, 28px); }
.fs-tpuff.is-on { opacity: 1; transform: translate(-50%, -50%) scale(1); }
@media (max-width: 760px) {
  /* One line, always — wrapping dropped the heart onto its own row and grew the
     cloud a second storey. The type gives way instead. (16 Aug 2026, Winnie) */
  .fs-toast {
    padding: 14px 22px 16px !important;
    white-space: nowrap;
    max-width: 92%;
    /* the reward reads at the same size as the thoughts (17 Aug 2026, Winnie) */
    font-size: var(--fs-cloud-fs);
  }
}
@media (prefers-reduced-motion: reduce) {
  .fs-tpuff { transition: opacity .3s linear; transform: translate(-50%, -50%); }
}


/* The heading and the portrait sit closer to the signatures: the names are
   drawn up toward the hairline rule rather than floating a line below it. */
@media (max-width: 900px) {
  .fs-intro .fs-onstage .founders-sig-block { margin-top: -22px !important; }
}


/* ---------------------------------------------------------------------------
   FULL SCREEN ON A PHONE
   Once the curtain is open the nursery takes the whole screen: the stage
   breaks out of the page's gutters to the viewport edges and stands as tall
   as the visible area allows. A 5:6 card in the middle of a phone gave the
   scene about a third of the glass to play in.
   --------------------------------------------------------------------------- */
@media (max-width: 760px) {
  .fs-stage {
    aspect-ratio: auto !important;
    width: 100vw;
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
    /* The whole frame has to sit BELOW the sticky bar and still end above the
       fold — so the bar's height comes off, plus a margin for the browser's
       own chrome. svh tracks the visible height as that chrome slides away. */
    height: calc(100vh  - var(--nav-height, 64px) - 26px);
    height: calc(100svh - var(--nav-height, 64px) - 26px);
    max-height: calc(100svh - var(--nav-height, 64px) - 26px);
    min-height: 460px;
    border-radius: 0;
    box-shadow: none;
  }
  /* the journey rail keeps the page's own margins */
  .fs-journey, .fs-journey-cap { padding-left: var(--gutter, 20px); padding-right: var(--gutter, 20px); }
}


/* ---------------------------------------------------------------------------
   CHAPTERS ON A PHONE — set inside the frame, not against it
   The columns ran to the frame's edges, which read as a wall of text in a box.
   They now sit inside a generous margin with the same edgeless veil the
   desktop panes use, and the frame itself never changes size between chapters.
   --------------------------------------------------------------------------- */
@media (max-width: 760px) {
  /* More air inside the veil (16 Aug 2026, Winnie). Three numbers set that
     margin and they move together: the chapter's padding holds the column off
     the frame, the veil's inset decides how far the light reaches past the
     words, and the gap between the stacked panes must stay wider than the veil
     or the two panels merge into one slab. */
  .fs-chapter {
    /* Trimmed top and bottom: on a phone that padding was 52px at each end,
       and it was 104px the words could have had. The side margins keep the
       measure comfortable. (17 Aug 2026, Winnie) */
    /* The veil starts 16px in and takes another 30px to fade to full white,
       so anything closer than ~46px is sitting on its soft edge. The words
       begin well past that on every side. (17 Aug 2026, Winnie) */
    /* Less at the top and bottom than at the sides: lines already carry their
       own leading, and every pixel given back there is a pixel the words can
       grow into. Still clear of the veil's soft edge (14 + 20 = 34px).
       (17 Aug 2026, Winnie) */
    padding: clamp(44px, 7vw, 56px) clamp(50px, 9vw, 66px) !important;
    display: flex; flex-direction: column; justify-content: center;
  }
  .fs-pane {
    width: 100%;
    margin: 0 0 clamp(18px, 4.5vw, 28px);
  }
  .fs-pane--b { margin-bottom: 0; }
  .fs-pane::before { inset: -28px -28px; }
  .fs-pane .fs-chapter-tag { margin-bottom: calc(16px * var(--fs-fit, 1)) !important; }
  .fs-continue { margin-top: calc(20px * var(--fs-fit, 1)) !important; }
}


/* the mark shows only while the curtain is up */
/* Shown exactly while the curtain is up. The stage carries the flag: the
   root's data-state passes through a literal "null" between scenes, so a
   state selector missed it entirely. */
.fs-stage.is-curtain .fs-mark { opacity: .78; }


/* ---------------------------------------------------------------------------
   THE TITLE PAGE, CENTRED IN THE FRAME
   `.fs-intro { position: relative }` comes after `.fs-layer { position:
   absolute; inset: 0 }` and wins, so the curtain's layer was only as tall as
   its own content (664px in a 774px frame) and its centring had nothing to
   centre against. Given the frame's full height, the block sits in the middle
   of the screen instead of riding the top edge.
   --------------------------------------------------------------------------- */
@media (max-width: 760px) {
  .fs-layer.fs-intro {
    min-height: 100%;
    justify-content: center;
  }
}


/* ---------------------------------------------------------------------------
   THE MARKS FLOAT THROUGH THE ROOM
   Held in a band across the top they left the whole lower half of the nursery
   empty, which read as a dashboard with a picture under it. They now hang at
   five places through the living space — never over the console at the foot of
   the frame, and never over one another.
   --------------------------------------------------------------------------- */
.fs-ms-wrap {
  inset: 0 !important;
  left: 0 !important; top: 0 !important;
  transform: none !important;
  width: 100% !important;
  display: block !important;
  pointer-events: none;                 /* only the marks themselves take taps */
}
.fs-ms {
  position: absolute !important;
  margin: 0 !important;
  pointer-events: auto;
  width: max-content;
  transform: translateX(-50%) !important;   /* nothing may unseat the ball */
}
.fs-ms:nth-child(1) { left: 11%; top: 15%; }
.fs-ms:nth-child(2) { left: 31%; top: 45%; }
.fs-ms:nth-child(3) { left: 57%; top: 12%; }
.fs-ms:nth-child(4) { left: 82%; top: 37%; }
.fs-ms:nth-child(5) { left: 43%; top: 66%; }

@media (max-width: 760px) {
  .fs-ms:nth-child(1) { left: 17%; top: 11%; }
  .fs-ms:nth-child(2) { left: 44%; top: 30%; }
  .fs-ms:nth-child(3) { left: 76%; top: 13%; }
  .fs-ms:nth-child(4) { left: 22%; top: 47%; }
  .fs-ms:nth-child(5) { left: 66%; top: 55%; }
}


/* ---------------------------------------------------------------------------
   SMALL, MEDIUM, LARGE — and standing on the furniture
   Script gives each mark its perch in the room; the sizes below give the group
   a rhythm rather than a scatter, so the five read as a set of glass balls
   decorating the nursery instead of five buttons at five random scales.
   --------------------------------------------------------------------------- */
.fs-ms:nth-child(1) { left: 16%; top: 41%; }     /* fallback before script runs */
.fs-ms:nth-child(2) { left: 45%; top: 45%; }
.fs-ms:nth-child(3) { left: 61%; top: 35%; }
.fs-ms:nth-child(4) { left: 90%; top: 35%; }
/* Pre-script fallback only; the live position comes from MARK_AT in the JS. */
.fs-ms:nth-child(5) { left: 38%; top: 78%; }

/* large - medium - large - small - medium */
.fs-ms:nth-child(1) .fs-ms-face { width: clamp(74px, 7.8vw, 104px); height: clamp(74px, 7.8vw, 104px); }
.fs-ms:nth-child(2) .fs-ms-face { width: clamp(58px, 6.1vw, 82px);  height: clamp(58px, 6.1vw, 82px);  }
.fs-ms:nth-child(3) .fs-ms-face { width: clamp(80px, 8.4vw, 112px); height: clamp(80px, 8.4vw, 112px); }
.fs-ms:nth-child(4) .fs-ms-face { width: clamp(50px, 5.2vw, 70px);  height: clamp(50px, 5.2vw, 70px);  }
.fs-ms:nth-child(5) .fs-ms-face { width: clamp(64px, 6.7vw, 90px);  height: clamp(64px, 6.7vw, 90px);  }

@media (max-width: 760px) {
  .fs-ms:nth-child(1) .fs-ms-face { width: 62px; height: 62px; }
  .fs-ms:nth-child(2) .fs-ms-face { width: 50px; height: 50px; }
  .fs-ms:nth-child(3) .fs-ms-face { width: 68px; height: 68px; }
  .fs-ms:nth-child(4) .fs-ms-face { width: 45px; height: 45px; }
  .fs-ms:nth-child(5) .fs-ms-face { width: 56px; height: 56px; }
}


/* ---------------------------------------------------------------------------
   PRESSING A MARK — a little burst of bubbles
   Seven tiny bubbles leave the ball at their own angles, drift outward, swell
   and pop. The ball itself stays exactly where it is.
   --------------------------------------------------------------------------- */
.fs-pop {
  position: absolute; left: 50%; top: 50%;
  width: var(--sz, 6px); height: var(--sz, 6px);
  border-radius: 50%;
  background:
    radial-gradient(circle at 32% 28%, rgba(255,255,255,.98) 0%, rgba(255,255,255,.25) 52%, rgba(255,255,255,0) 100%),
    rgba(255,255,255,.16);
  box-shadow: 0 0 0 1px rgba(255,255,255,.85), inset 0 0 5px rgba(255,255,255,.7);
  pointer-events: none;
  opacity: 0;
  animation-name: fsPopRise;
  animation-timing-function: cubic-bezier(.2,.7,.35,1);
  animation-fill-mode: forwards;
  z-index: 4;
}
@keyframes fsPopRise {
  0%   { opacity: 0;   transform: translate(-50%, -50%) rotate(var(--a, 0deg)) translateX(2px)  scale(.35); }
  18%  { opacity: .95; }
  72%  { opacity: .95; transform: translate(-50%, -50%) rotate(var(--a, 0deg)) translateX(var(--far, 38px)) scale(1); }
  100% { opacity: 0;   transform: translate(-50%, -50%) rotate(var(--a, 0deg)) translateX(calc(var(--far, 38px) + 10px)) scale(1.75); }
}
@media (prefers-reduced-motion: reduce) { .fs-pop { display: none; } }


/* Scrolled to, the frame lands just under the bar rather than behind it. */
.fs-root { scroll-margin-top: calc(var(--nav-height, 64px) + 8px); }


/* ---------------------------------------------------------------------------
   THE CONSOLE STEPS ASIDE (16 Aug 2026, Winnie)
   Centred along the bottom it covered any mark resting on the carpet — Tummy
   Time could not be tapped between 760px and 1440px. The floor either side is
   already taken by the room's own toys, so the console moves instead.
   --------------------------------------------------------------------------- */
/* SUPERSEDED 24 Aug 2026 (Winnie: "place this at the center of the curtain
   window"). The console is centred along the bottom again. The reason it was
   moved aside in the first place — it covered Tummy Time, which sat on the
   carpet at x 0.389 — is dealt with by lifting that mark instead: Tummy Time
   is now above the console's top edge, so both stay tappable.
   To put the console back on the right, restore the two lines below and move
   Tummy Time's `wide` entry back down toward y 0.864. */
@media (min-width: 701px) {
  .fs-console {
    left: 50%;
    right: auto;
    transform: translateX(-50%);
  }
}


/* ---------------------------------------------------------------------------
   THE THREE THOUGHTS, ON A PHONE (24 Aug 2026, Winnie)
   "all the thinking cloud 3 bubble been clog together". At 10px apart, with
   blurred edges on a high-density screen, the puffs merged into one shape.
   The big circle grows and the two shadow spreads widen, so the chain steps
   7px -> 12px -> 18px on its way up to the cloud; founding-story.js widens the
   gap between them to 15px to match. The two files share these numbers — the
   spreads below ARE the middle and small diameters, so change them together.
   --------------------------------------------------------------------------- */
@media (max-width: 760px) {
  /* Same four-puff ladder, one size up because a phone screen is denser:
     19 - 14 - 10 - 7.5. Spreads are (diameter - 19) / 2. (24 Aug 2026) */
  .fs-cue::after {
    width: 19px; height: 19px; margin-left: -9.5px;
    box-shadow: var(--fs-tp3x, -5px)  var(--fs-mid-y,   26px) 0 -2.5px  rgba(255, 255, 255, 0.93),
                var(--fs-tp2x, -10px) var(--fs-small-y, 48px) 0 -4.5px  rgba(255, 255, 255, 0.92),
                var(--fs-tp1x, -14px) var(--fs-tiny-y,  68px) 0 -5.75px rgba(255, 255, 255, 0.91),
                0 6px 12px rgba(150, 130, 165, 0.16);
  }
}

/* ==========================================================================
   ONE COLUMN ON DESKTOP TOO (24 Aug 2026, Winnie)
   "put this in one script like the mobile view did. same frame, same one page
    and the wording not to tight between left and right of the white
    transparent background."

   A desktop chapter used to be cut into two floating panes set into the room's
   empty space. It is now the phone's single pane, given a desktop measure.

   The breathing room she asked for is the pane's own padding: the veil is
   pinned to the pane's box (inset: 0) rather than bleeding past it, so the
   padding below IS the gap between the words and the white on every side.

   splitChapter() in founding-story.js now always builds the solo pane; its
   two-column branch is still there if this is ever reversed.
   ========================================================================== */
@media (min-width: 761px) {
  .fs-chapter {
    position: absolute;
    inset: 0;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: clamp(20px, 2.6vw, 44px);
  }

  .fs-pane--solo {
    position: relative;
    z-index: 1;
    left: auto; right: auto; top: auto; bottom: auto;
    width: min(70%, 760px);
    max-width: 100%;
    margin: 0;
    box-sizing: border-box;
    /* the measure stays readable and the words never touch the white */
    padding: clamp(30px, 3.4vw, 54px) clamp(40px, 5vw, 80px);
  }

  /* The veil matches the column's own box now. The desktop default bleeds it
     outward by -13%, which on a column this wide would have run past the
     frame; here it is the pane, feathered in pixels so both flanks dissolve by
     the same amount whatever the frame's size. */
  .fs-pane--solo::before {
    content: '';
    inset: 0;
    border-radius: 26px;
    -webkit-mask-image:
      linear-gradient(to right,  transparent 0, #000 30px, #000 calc(100% - 30px), transparent 100%),
      linear-gradient(to bottom, transparent 0, #000 26px, #000 calc(100% - 26px), transparent 100%);
            mask-image:
      linear-gradient(to right,  transparent 0, #000 30px, #000 calc(100% - 30px), transparent 100%),
      linear-gradient(to bottom, transparent 0, #000 26px, #000 calc(100% - 26px), transparent 100%);
    -webkit-mask-composite: source-in;
            mask-composite: intersect;
  }

  /* A pair of columns drifted on separate beats so they never read as one
     block. One column has nothing to read against, so it simply settles. */
  .fs-root.is-live .fs-pane--solo {
    animation: fsPaneIn .9s var(--fs-ease) both;
  }

  /* "Continue our journey" sits in the middle of the white box on every
     chapter (24 Aug 2026, Winnie). It is inline-block, so auto margins need a
     block box and an intrinsic width to work against — the same fix the phone
     already carries for this button. */
  .fs-pane--solo .fs-continue {
    display: block;
    width: -moz-max-content;
    width: max-content;
    max-width: 100%;
    margin-left: auto;
    margin-right: auto;
  }
}

/* ---------------------------------------------------------------------------
   HOVER IS FOR MICE ONLY (26 Aug 2026, Winnie: "i have to click twice in order
   for the 'continue to our journey' for every session of it to go through next
   page." / "when playing the simulation little game under about us, all need to
   press twice.")

   Every control in the story carried a :hover rule that MOVES it — .fs-btn and
   .fs-continue lift 2px, .fs-ck and .fs-ck-begin lift 1px, the milestone face
   scales 1.06 — each over a .35s transition. A touch screen has no hover, so
   the browser fakes one: the first tap applies :hover, the control slides out
   from under the finger while the transition runs, the finger lifts somewhere
   the control no longer is, and no click is delivered. The second tap works
   because :hover is already stuck on and nothing moves. Every affected control
   shares this one pattern, which is why every one of them needed two presses.

   The fix is to let hover styling apply only where a real hovering pointer
   exists. Nothing is removed for desktop; touch simply stops being told it is
   hovering. This is CSS only — no handler, no state machine and no part of the
   simulation is touched.

   NOTE ON HOW THIS WAS ARRIVED AT: it could NOT be reproduced in a headless
   browser with emulated touch — there, one tap works. Emulated touch does not
   reproduce a real handset's sticky-hover behaviour, so absence of a repro here
   is not evidence of absence. This mechanism is the only one that explains
   EVERY control failing identically; the two other usual suspects were checked
   and ruled out: the viewport meta is correct on all 12 pages (so there is no
   legacy 300ms tap delay), and no ancestor blocks taps via touch-action.
   If two presses are still needed after this ships, the next thing to examine
   is a transparent element sitting over the controls, not the CSS below.
   --------------------------------------------------------------------------- */
@media (hover: none), (pointer: coarse) {
  .fs-btn:hover,
  .fs-ck:hover,
  .fs-ck-begin:hover,
  .fs-continue:hover      { transform: none; }
  .fs-ms:hover .fs-ms-face svg { transform: none; }

  /* Nothing should be mid-transition when the finger lifts. */
  .fs-btn, .fs-ck, .fs-ck-begin, .fs-continue { transition: none; }
}
