/* ============================================================
   INTRO — logo-shaped video reveal (the zipline.com opener).
   The video is full-bleed from the first frame; what animates is
   the cream sheet on top of it, which has the wordmark knocked
   out of itself. The hole starts at logo size and grows until it
   has eaten the sheet — the sheet never fades, it is wiped.
   ============================================================ */

/* the stage pins while the slogan fills; one extra screen of scroll is
   all that takes */
/* three screens of track: the film shrinks over the first, then the
   slogan is read out over the rest */
/* one number for the film's landing corner — the slot and the film both
   read it, so they can never disagree.

   🔴 FLUID, not a flat 16px. The slot is sized off the headline's own em,
   so a fixed corner is a different SHAPE at every width: on the 1440 slot
   (101px tall) 16px is a .16 corner, on the 700 slot (49px) the same 16px
   is .33, and at phone size the slot is 17px tall — past h/2, so the
   browser clamps it and the film renders as a PILL. The corner stopped
   being a corner. .118 of the headline's font-size holds the 1440 shape
   at every width; written in vw because intro.js has to read this as a
   real length. 1.12vw is 16.1px at 1440, so desktop is where it was. */
:root { --film-r: clamp(4.5px, 1.12vw, 20px); }

.intro {
  position: relative;
  /* the dock takes the first stretch, the four hours the rest — four
     states need room to be read, so the track is longer than a shrink
     alone would need.

     🔴 900, up from 560, for the same reason the deck below went to
     300svh per slide: RESISTANCE IS BOUGHT WITH DISTANCE. At 560 the
     whole of scene two — the film docked in the line, "Time … to just be
     a parent." coming up, the glow closing down onto the clock — fitted
     in about four viewports, which one trackpad flick clears. Every mark
     in intro.js is a FRACTION of this track (SHRINK_END .30, COPY_FROM
     .28, SUN_FROM/UP/CLOSE), so raising the height stretches all of them
     together and nothing has to be retuned:

         shrink   0 → .30    1.7 → 2.7 viewports
         scene 2  .28 → 1    4.0 → 6.5 viewports  */
  height: 900svh;
  background-color: var(--page-bg);   /* the one ground — see --page-bg in styles.css */
  color: var(--nes-ink);
  overflow-x: clip;
}

.intro__stage {
  position: sticky; top: 0;
  height: 100svh;
  min-height: 620px;
  overflow: hidden;
  isolation: isolate;
  /* 🔴 z-index 2, and NO background of its own — the ground moved up to
     .intro. The halo is a fixed layer at z-index 1, so it needs something
     to sit above (the section's colour) and something to sit below (this
     stage's contents). With the colour painted here instead, the halo had
     nowhere to go: over the stage it covered the headline, under it it was
     invisible. */
  z-index: 2;
}

/* ---------- the film ----------
   It is the SAME element from full screen to tile — never swapped for a
   smaller copy, which is what keeps the shot continuous. JS drives --mw
   only; the height comes off the aspect ratio and the corner off --mr, so
   there is one number to interpolate and nothing to keep in step.

   Centred with left/top 50% and a translate, so shrinking is automatically
   about the middle of the frame: at every size the gap either side is
   (stage − width) / 2 without anyone computing it. */
/* ---------- the film ----------
   🔴 It moves on TRANSFORM ONLY — no width, no top, no left. Those are
   layout properties: animating them re-lays-out and repaints the whole
   stage every frame, which is exactly what drops a scroll-scrubbed shot
   below 60fps. A translate + a uniform scale is a compositor job.

   The box is therefore a FIXED 16:10 rectangle the width of the stage,
   centred, and the scrub only ever changes its transform. Uniform scale
   throughout, so the picture is never stretched — at the start it is
   scaled UP just enough to cover a viewport taller than 16:10, and at
   the end scaled down onto the slot.

   🔴 No position:fixed and no FLIP hand-off, despite the obvious
   temptation: the stage is already `position: sticky`, so while the
   section is pinned this box behaves exactly like a fixed one AND it
   still scrolls away correctly when the section releases. Making it
   fixed would mean re-parenting it on arrival to stop it hanging over
   the next section — solving a problem the sticky stage does not have. */
.intro__media {
  position: absolute; z-index: 2;
  left: 0; top: 50%;
  width: 100%;
  aspect-ratio: 16 / 10;
  transform-origin: 50% 50%;
  transform: translate3d(0, -50%, 0);
  border-radius: var(--mr, 0px);
  overflow: hidden;
  will-change: transform;
}
.intro__video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}

/* the sheet with the wordmark cut out of it.
   🔴 The mask is INLINE, not url(assets/shapes/logo.svg). Chrome treats
   every file:// document as its own opaque origin, so an SVG loaded as a
   mask from a sibling file is refused — mask-image resolves to none, the
   sheet stays solid, and the opener plays as a blank cream screen. A
   data: URL is same-origin everywhere, so this works off the filesystem
   as well as off a server. assets/shapes/logo.svg stays as the source. */
.intro__cover {
  position: absolute; inset: 0; z-index: 3;
  background: var(--page-bg);   /* the sheet must BE the ground it wipes off */
  --logo: clamp(240px, 44vw, 620px);

  -webkit-mask-image: linear-gradient(#000, #000), url("data:image/svg+xml,%3Csvg width='421' height='115' viewBox='8 42 421 115' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M78.592 131.312C77.3973 132.507 75.648 133.403 73.344 134C71.1253 134.683 68.4373 135.024 65.28 135.024C62.1227 135.024 59.136 134.555 56.32 133.616C53.504 132.677 51.2 130.416 49.408 126.832L36.736 100.976C35.456 98.3307 34.304 95.856 33.28 93.552C32.256 91.1627 31.1467 88.432 29.952 85.36L29.184 85.488C29.5253 90.096 29.696 94.7893 29.696 99.568C29.7813 104.347 29.824 109.04 29.824 113.648V133.488C28.8853 133.744 27.4347 134 25.472 134.256C23.5947 134.512 21.7173 134.64 19.84 134.64C17.9627 134.64 16.2987 134.512 14.848 134.256C13.3973 134 12.2027 133.488 11.264 132.72C10.3253 131.952 9.6 130.928 9.088 129.648C8.576 128.283 8.32 126.533 8.32 124.4V60.4C9.51467 58.6933 11.3493 57.4133 13.824 56.56C16.2987 55.7067 18.944 55.28 21.76 55.28C24.9173 55.28 27.904 55.792 30.72 56.816C33.6213 57.7547 35.9253 59.9733 37.632 63.472L50.432 89.328C51.712 91.9733 52.864 94.4907 53.888 96.88C54.912 99.184 56.0213 101.872 57.216 104.944L57.856 104.816C57.5147 100.208 57.3013 95.6 57.216 90.992C57.1307 86.384 57.088 81.776 57.088 77.168V56.432C58.0267 56.176 59.4347 55.92 61.312 55.664C63.2747 55.408 65.1947 55.28 67.072 55.28C70.8267 55.28 73.6853 55.9627 75.648 57.328C77.6107 58.608 78.592 61.3387 78.592 65.52V131.312ZM126.427 136.048C121.392 136.048 116.699 135.365 112.347 134C108.08 132.549 104.326 130.416 101.083 127.6C97.9257 124.784 95.4083 121.243 93.531 116.976C91.739 112.709 90.843 107.717 90.843 102C90.843 96.368 91.739 91.5467 93.531 87.536C95.4083 83.44 97.8403 80.112 100.827 77.552C103.814 74.9067 107.227 72.9867 111.067 71.792C114.907 70.512 118.832 69.872 122.843 69.872C127.366 69.872 131.462 70.5547 135.131 71.92C138.886 73.2853 142.086 75.1627 144.731 77.552C147.462 79.9413 149.552 82.8 151.003 86.128C152.539 89.456 153.307 93.0827 153.307 97.008C153.307 99.9093 152.496 102.128 150.875 103.664C149.254 105.2 146.992 106.181 144.091 106.608L112.475 111.344C113.414 114.16 115.334 116.293 118.235 117.744C121.136 119.109 124.464 119.792 128.219 119.792C131.718 119.792 135.003 119.365 138.075 118.512C141.232 117.573 143.792 116.507 145.755 115.312C147.12 116.165 148.272 117.36 149.211 118.896C150.15 120.432 150.619 122.053 150.619 123.76C150.619 127.6 148.827 130.459 145.243 132.336C142.512 133.787 139.44 134.768 136.027 135.28C132.614 135.792 129.414 136.048 126.427 136.048ZM122.843 85.744C120.795 85.744 119.003 86.0853 117.467 86.768C116.016 87.4507 114.822 88.3467 113.883 89.456C112.944 90.48 112.219 91.6747 111.707 93.04C111.28 94.32 111.024 95.6427 110.939 97.008L132.827 93.424C132.571 91.7173 131.632 90.0107 130.011 88.304C128.39 86.5973 126 85.744 122.843 85.744ZM216.261 115.44C216.261 121.925 213.829 127.003 208.965 130.672C204.101 134.341 196.933 136.176 187.461 136.176C183.877 136.176 180.549 135.92 177.477 135.408C174.405 134.896 171.76 134.128 169.541 133.104C167.408 131.995 165.701 130.629 164.421 129.008C163.226 127.387 162.629 125.467 162.629 123.248C162.629 121.2 163.056 119.493 163.909 118.128C164.762 116.677 165.786 115.483 166.981 114.544C169.456 115.909 172.272 117.147 175.429 118.256C178.672 119.28 182.384 119.792 186.565 119.792C189.21 119.792 191.216 119.408 192.581 118.64C194.032 117.872 194.757 116.848 194.757 115.568C194.757 114.373 194.245 113.435 193.221 112.752C192.197 112.069 190.49 111.515 188.101 111.088L184.261 110.32C176.837 108.869 171.29 106.608 167.621 103.536C164.037 100.379 162.245 95.8987 162.245 90.096C162.245 86.9387 162.928 84.08 164.293 81.52C165.658 78.96 167.578 76.8267 170.053 75.12C172.528 73.4133 175.472 72.0907 178.885 71.152C182.384 70.2133 186.224 69.744 190.405 69.744C193.562 69.744 196.506 70 199.237 70.512C202.053 70.9387 204.485 71.6213 206.533 72.56C208.581 73.4987 210.202 74.736 211.397 76.272C212.592 77.7227 213.189 79.472 213.189 81.52C213.189 83.4827 212.805 85.1893 212.037 86.64C211.354 88.0053 210.458 89.1573 209.349 90.096C208.666 89.6693 207.642 89.2427 206.277 88.816C204.912 88.304 203.418 87.8773 201.797 87.536C200.176 87.1093 198.512 86.768 196.805 86.512C195.184 86.256 193.69 86.128 192.325 86.128C189.509 86.128 187.333 86.4693 185.797 87.152C184.261 87.7493 183.493 88.7307 183.493 90.096C183.493 91.0347 183.92 91.8027 184.773 92.4C185.626 92.9973 187.248 93.552 189.637 94.064L193.605 94.96C201.797 96.8373 207.6 99.44 211.013 102.768C214.512 106.011 216.261 110.235 216.261 115.44ZM248.541 112.624C248.541 114.843 249.224 116.421 250.589 117.36C252.04 118.299 254.045 118.768 256.605 118.768C257.885 118.768 259.208 118.683 260.573 118.512C261.938 118.256 263.133 117.957 264.157 117.616C264.925 118.555 265.565 119.621 266.077 120.816C266.674 121.925 266.973 123.291 266.973 124.912C266.973 128.155 265.736 130.8 263.261 132.848C260.872 134.896 256.605 135.92 250.461 135.92C242.952 135.92 237.149 134.213 233.053 130.8C229.042 127.387 227.037 121.84 227.037 114.16V59.376C227.976 59.12 229.298 58.864 231.005 58.608C232.797 58.2667 234.674 58.096 236.637 58.096C240.392 58.096 243.293 58.7787 245.341 60.144C247.474 61.424 248.541 64.1973 248.541 68.464V76.656H265.309C265.821 77.5947 266.29 78.7893 266.717 80.24C267.229 81.6053 267.485 83.1413 267.485 84.848C267.485 87.8347 266.802 90.0107 265.437 91.376C264.157 92.656 262.408 93.296 260.189 93.296H248.541V112.624ZM277.519 54.512C277.519 51.2693 278.586 48.5387 280.719 46.32C282.938 44.1013 285.839 42.992 289.423 42.992C293.007 42.992 295.866 44.1013 297.999 46.32C300.218 48.5387 301.327 51.2693 301.327 54.512C301.327 57.7547 300.218 60.4853 297.999 62.704C295.866 64.9227 293.007 66.032 289.423 66.032C285.839 66.032 282.938 64.9227 280.719 62.704C278.586 60.4853 277.519 57.7547 277.519 54.512ZM300.303 133.872C299.364 134.043 297.956 134.256 296.079 134.512C294.287 134.853 292.452 135.024 290.575 135.024C288.698 135.024 286.991 134.896 285.455 134.64C284.004 134.384 282.767 133.872 281.743 133.104C280.719 132.336 279.908 131.312 279.311 130.032C278.799 128.667 278.543 126.917 278.543 124.784V72.688C279.482 72.5173 280.847 72.304 282.639 72.048C284.516 71.7067 286.394 71.536 288.271 71.536C290.148 71.536 291.812 71.664 293.263 71.92C294.799 72.176 296.079 72.688 297.103 73.456C298.127 74.224 298.895 75.2907 299.407 76.656C300.004 77.936 300.303 79.6427 300.303 81.776V133.872ZM314.412 71.792C314.412 67.44 315.137 63.728 316.588 60.656C318.124 57.4987 320.172 54.9387 322.732 52.976C325.292 50.928 328.279 49.4347 331.692 48.496C335.191 47.5573 338.903 47.088 342.828 47.088C349.143 47.088 353.793 47.9413 356.78 49.648C359.767 51.2693 361.26 54.0853 361.26 58.096C361.26 59.8027 360.876 61.3387 360.108 62.704C359.425 64.0693 358.657 65.0933 357.804 65.776C356.183 65.3493 354.476 65.008 352.684 64.752C350.977 64.4107 348.972 64.24 346.668 64.24C342.999 64.24 340.14 64.88 338.092 66.16C336.044 67.3547 335.02 69.488 335.02 72.56V76.656H356.012C356.524 77.5947 356.993 78.7893 357.42 80.24C357.932 81.6053 358.188 83.1413 358.188 84.848C358.188 87.8347 357.505 90.0107 356.14 91.376C354.86 92.656 353.111 93.296 350.892 93.296H336.172V133.872C335.319 134.128 333.953 134.384 332.076 134.64C330.284 134.896 328.407 135.024 326.444 135.024C324.567 135.024 322.86 134.896 321.324 134.64C319.873 134.384 318.636 133.872 317.612 133.104C316.588 132.336 315.777 131.312 315.18 130.032C314.668 128.667 314.412 126.917 314.412 124.784V71.792ZM377.634 118.128C376.695 115.995 375.671 113.605 374.562 110.96C373.538 108.315 372.429 105.285 371.234 101.872C370.125 98.4587 368.93 94.576 367.65 90.224C366.455 85.7867 365.175 80.7093 363.81 74.992C365.005 73.7973 366.583 72.7733 368.546 71.92C370.594 70.9813 372.813 70.512 375.202 70.512C378.189 70.512 380.663 71.152 382.626 72.432C384.589 73.6267 386.039 75.9307 386.978 79.344L397.09 114.544H397.602C398.626 111.643 399.607 108.485 400.546 105.072C401.57 101.659 402.551 98.16 403.49 94.576C404.429 90.9067 405.282 87.28 406.05 83.696C406.903 80.0267 407.629 76.4427 408.226 72.944C411.298 71.3227 414.669 70.512 418.338 70.512C421.325 70.512 423.799 71.152 425.762 72.432C427.725 73.712 428.706 75.9307 428.706 79.088C428.706 81.392 428.407 84.08 427.81 87.152C427.213 90.1387 426.402 93.3387 425.378 96.752C424.439 100.165 423.287 103.707 421.922 107.376C420.642 110.96 419.277 114.501 417.826 118C416.375 121.499 414.882 124.827 413.346 127.984C411.81 131.056 410.317 133.829 408.866 136.304C406.477 140.485 404.258 143.899 402.21 146.544C400.162 149.189 398.199 151.237 396.322 152.688C394.445 154.224 392.567 155.248 390.69 155.76C388.813 156.357 386.85 156.656 384.802 156.656C381.218 156.656 378.317 155.589 376.098 153.456C373.879 151.323 372.557 148.549 372.13 145.136C374.861 143.003 377.591 140.613 380.322 137.968C383.053 135.408 385.527 132.763 387.746 130.032C386.295 129.605 384.717 128.624 383.01 127.088C381.389 125.467 379.597 122.48 377.634 118.128Z' fill='%23fff'/%3E%3Ccircle cx='289.5' cy='54.5' r='12.5' fill='%23fff'/%3E%3C/svg%3E");
          mask-image: linear-gradient(#000, #000), url("data:image/svg+xml,%3Csvg width='421' height='115' viewBox='8 42 421 115' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M78.592 131.312C77.3973 132.507 75.648 133.403 73.344 134C71.1253 134.683 68.4373 135.024 65.28 135.024C62.1227 135.024 59.136 134.555 56.32 133.616C53.504 132.677 51.2 130.416 49.408 126.832L36.736 100.976C35.456 98.3307 34.304 95.856 33.28 93.552C32.256 91.1627 31.1467 88.432 29.952 85.36L29.184 85.488C29.5253 90.096 29.696 94.7893 29.696 99.568C29.7813 104.347 29.824 109.04 29.824 113.648V133.488C28.8853 133.744 27.4347 134 25.472 134.256C23.5947 134.512 21.7173 134.64 19.84 134.64C17.9627 134.64 16.2987 134.512 14.848 134.256C13.3973 134 12.2027 133.488 11.264 132.72C10.3253 131.952 9.6 130.928 9.088 129.648C8.576 128.283 8.32 126.533 8.32 124.4V60.4C9.51467 58.6933 11.3493 57.4133 13.824 56.56C16.2987 55.7067 18.944 55.28 21.76 55.28C24.9173 55.28 27.904 55.792 30.72 56.816C33.6213 57.7547 35.9253 59.9733 37.632 63.472L50.432 89.328C51.712 91.9733 52.864 94.4907 53.888 96.88C54.912 99.184 56.0213 101.872 57.216 104.944L57.856 104.816C57.5147 100.208 57.3013 95.6 57.216 90.992C57.1307 86.384 57.088 81.776 57.088 77.168V56.432C58.0267 56.176 59.4347 55.92 61.312 55.664C63.2747 55.408 65.1947 55.28 67.072 55.28C70.8267 55.28 73.6853 55.9627 75.648 57.328C77.6107 58.608 78.592 61.3387 78.592 65.52V131.312ZM126.427 136.048C121.392 136.048 116.699 135.365 112.347 134C108.08 132.549 104.326 130.416 101.083 127.6C97.9257 124.784 95.4083 121.243 93.531 116.976C91.739 112.709 90.843 107.717 90.843 102C90.843 96.368 91.739 91.5467 93.531 87.536C95.4083 83.44 97.8403 80.112 100.827 77.552C103.814 74.9067 107.227 72.9867 111.067 71.792C114.907 70.512 118.832 69.872 122.843 69.872C127.366 69.872 131.462 70.5547 135.131 71.92C138.886 73.2853 142.086 75.1627 144.731 77.552C147.462 79.9413 149.552 82.8 151.003 86.128C152.539 89.456 153.307 93.0827 153.307 97.008C153.307 99.9093 152.496 102.128 150.875 103.664C149.254 105.2 146.992 106.181 144.091 106.608L112.475 111.344C113.414 114.16 115.334 116.293 118.235 117.744C121.136 119.109 124.464 119.792 128.219 119.792C131.718 119.792 135.003 119.365 138.075 118.512C141.232 117.573 143.792 116.507 145.755 115.312C147.12 116.165 148.272 117.36 149.211 118.896C150.15 120.432 150.619 122.053 150.619 123.76C150.619 127.6 148.827 130.459 145.243 132.336C142.512 133.787 139.44 134.768 136.027 135.28C132.614 135.792 129.414 136.048 126.427 136.048ZM122.843 85.744C120.795 85.744 119.003 86.0853 117.467 86.768C116.016 87.4507 114.822 88.3467 113.883 89.456C112.944 90.48 112.219 91.6747 111.707 93.04C111.28 94.32 111.024 95.6427 110.939 97.008L132.827 93.424C132.571 91.7173 131.632 90.0107 130.011 88.304C128.39 86.5973 126 85.744 122.843 85.744ZM216.261 115.44C216.261 121.925 213.829 127.003 208.965 130.672C204.101 134.341 196.933 136.176 187.461 136.176C183.877 136.176 180.549 135.92 177.477 135.408C174.405 134.896 171.76 134.128 169.541 133.104C167.408 131.995 165.701 130.629 164.421 129.008C163.226 127.387 162.629 125.467 162.629 123.248C162.629 121.2 163.056 119.493 163.909 118.128C164.762 116.677 165.786 115.483 166.981 114.544C169.456 115.909 172.272 117.147 175.429 118.256C178.672 119.28 182.384 119.792 186.565 119.792C189.21 119.792 191.216 119.408 192.581 118.64C194.032 117.872 194.757 116.848 194.757 115.568C194.757 114.373 194.245 113.435 193.221 112.752C192.197 112.069 190.49 111.515 188.101 111.088L184.261 110.32C176.837 108.869 171.29 106.608 167.621 103.536C164.037 100.379 162.245 95.8987 162.245 90.096C162.245 86.9387 162.928 84.08 164.293 81.52C165.658 78.96 167.578 76.8267 170.053 75.12C172.528 73.4133 175.472 72.0907 178.885 71.152C182.384 70.2133 186.224 69.744 190.405 69.744C193.562 69.744 196.506 70 199.237 70.512C202.053 70.9387 204.485 71.6213 206.533 72.56C208.581 73.4987 210.202 74.736 211.397 76.272C212.592 77.7227 213.189 79.472 213.189 81.52C213.189 83.4827 212.805 85.1893 212.037 86.64C211.354 88.0053 210.458 89.1573 209.349 90.096C208.666 89.6693 207.642 89.2427 206.277 88.816C204.912 88.304 203.418 87.8773 201.797 87.536C200.176 87.1093 198.512 86.768 196.805 86.512C195.184 86.256 193.69 86.128 192.325 86.128C189.509 86.128 187.333 86.4693 185.797 87.152C184.261 87.7493 183.493 88.7307 183.493 90.096C183.493 91.0347 183.92 91.8027 184.773 92.4C185.626 92.9973 187.248 93.552 189.637 94.064L193.605 94.96C201.797 96.8373 207.6 99.44 211.013 102.768C214.512 106.011 216.261 110.235 216.261 115.44ZM248.541 112.624C248.541 114.843 249.224 116.421 250.589 117.36C252.04 118.299 254.045 118.768 256.605 118.768C257.885 118.768 259.208 118.683 260.573 118.512C261.938 118.256 263.133 117.957 264.157 117.616C264.925 118.555 265.565 119.621 266.077 120.816C266.674 121.925 266.973 123.291 266.973 124.912C266.973 128.155 265.736 130.8 263.261 132.848C260.872 134.896 256.605 135.92 250.461 135.92C242.952 135.92 237.149 134.213 233.053 130.8C229.042 127.387 227.037 121.84 227.037 114.16V59.376C227.976 59.12 229.298 58.864 231.005 58.608C232.797 58.2667 234.674 58.096 236.637 58.096C240.392 58.096 243.293 58.7787 245.341 60.144C247.474 61.424 248.541 64.1973 248.541 68.464V76.656H265.309C265.821 77.5947 266.29 78.7893 266.717 80.24C267.229 81.6053 267.485 83.1413 267.485 84.848C267.485 87.8347 266.802 90.0107 265.437 91.376C264.157 92.656 262.408 93.296 260.189 93.296H248.541V112.624ZM277.519 54.512C277.519 51.2693 278.586 48.5387 280.719 46.32C282.938 44.1013 285.839 42.992 289.423 42.992C293.007 42.992 295.866 44.1013 297.999 46.32C300.218 48.5387 301.327 51.2693 301.327 54.512C301.327 57.7547 300.218 60.4853 297.999 62.704C295.866 64.9227 293.007 66.032 289.423 66.032C285.839 66.032 282.938 64.9227 280.719 62.704C278.586 60.4853 277.519 57.7547 277.519 54.512ZM300.303 133.872C299.364 134.043 297.956 134.256 296.079 134.512C294.287 134.853 292.452 135.024 290.575 135.024C288.698 135.024 286.991 134.896 285.455 134.64C284.004 134.384 282.767 133.872 281.743 133.104C280.719 132.336 279.908 131.312 279.311 130.032C278.799 128.667 278.543 126.917 278.543 124.784V72.688C279.482 72.5173 280.847 72.304 282.639 72.048C284.516 71.7067 286.394 71.536 288.271 71.536C290.148 71.536 291.812 71.664 293.263 71.92C294.799 72.176 296.079 72.688 297.103 73.456C298.127 74.224 298.895 75.2907 299.407 76.656C300.004 77.936 300.303 79.6427 300.303 81.776V133.872ZM314.412 71.792C314.412 67.44 315.137 63.728 316.588 60.656C318.124 57.4987 320.172 54.9387 322.732 52.976C325.292 50.928 328.279 49.4347 331.692 48.496C335.191 47.5573 338.903 47.088 342.828 47.088C349.143 47.088 353.793 47.9413 356.78 49.648C359.767 51.2693 361.26 54.0853 361.26 58.096C361.26 59.8027 360.876 61.3387 360.108 62.704C359.425 64.0693 358.657 65.0933 357.804 65.776C356.183 65.3493 354.476 65.008 352.684 64.752C350.977 64.4107 348.972 64.24 346.668 64.24C342.999 64.24 340.14 64.88 338.092 66.16C336.044 67.3547 335.02 69.488 335.02 72.56V76.656H356.012C356.524 77.5947 356.993 78.7893 357.42 80.24C357.932 81.6053 358.188 83.1413 358.188 84.848C358.188 87.8347 357.505 90.0107 356.14 91.376C354.86 92.656 353.111 93.296 350.892 93.296H336.172V133.872C335.319 134.128 333.953 134.384 332.076 134.64C330.284 134.896 328.407 135.024 326.444 135.024C324.567 135.024 322.86 134.896 321.324 134.64C319.873 134.384 318.636 133.872 317.612 133.104C316.588 132.336 315.777 131.312 315.18 130.032C314.668 128.667 314.412 126.917 314.412 124.784V71.792ZM377.634 118.128C376.695 115.995 375.671 113.605 374.562 110.96C373.538 108.315 372.429 105.285 371.234 101.872C370.125 98.4587 368.93 94.576 367.65 90.224C366.455 85.7867 365.175 80.7093 363.81 74.992C365.005 73.7973 366.583 72.7733 368.546 71.92C370.594 70.9813 372.813 70.512 375.202 70.512C378.189 70.512 380.663 71.152 382.626 72.432C384.589 73.6267 386.039 75.9307 386.978 79.344L397.09 114.544H397.602C398.626 111.643 399.607 108.485 400.546 105.072C401.57 101.659 402.551 98.16 403.49 94.576C404.429 90.9067 405.282 87.28 406.05 83.696C406.903 80.0267 407.629 76.4427 408.226 72.944C411.298 71.3227 414.669 70.512 418.338 70.512C421.325 70.512 423.799 71.152 425.762 72.432C427.725 73.712 428.706 75.9307 428.706 79.088C428.706 81.392 428.407 84.08 427.81 87.152C427.213 90.1387 426.402 93.3387 425.378 96.752C424.439 100.165 423.287 103.707 421.922 107.376C420.642 110.96 419.277 114.501 417.826 118C416.375 121.499 414.882 124.827 413.346 127.984C411.81 131.056 410.317 133.829 408.866 136.304C406.477 140.485 404.258 143.899 402.21 146.544C400.162 149.189 398.199 151.237 396.322 152.688C394.445 154.224 392.567 155.248 390.69 155.76C388.813 156.357 386.85 156.656 384.802 156.656C381.218 156.656 378.317 155.589 376.098 153.456C373.879 151.323 372.557 148.549 372.13 145.136C374.861 143.003 377.591 140.613 380.322 137.968C383.053 135.408 385.527 132.763 387.746 130.032C386.295 129.605 384.717 128.624 383.01 127.088C381.389 125.467 379.597 122.48 377.634 118.128Z' fill='%23fff'/%3E%3Ccircle cx='289.5' cy='54.5' r='12.5' fill='%23fff'/%3E%3C/svg%3E");
  -webkit-mask-size: 100% 100%, var(--logo) auto;
          mask-size: 100% 100%, var(--logo) auto;
  -webkit-mask-position: center, center;
          mask-position: center, center;
  -webkit-mask-repeat: no-repeat, no-repeat;
          mask-repeat: no-repeat, no-repeat;
  -webkit-mask-composite: xor;          /* Safari */
          mask-composite: exclude;      /* everyone else */

  /* the dot on the i, in the element's own coordinates. The mask is
     centred, so the dot sits half a logo-width right of centre and a
     little above it:
       x = 50% + L·((289.5−8)/421 − 0.5)          = 50% + 0.16865·L
       y = 50% − L·(115/421)·(0.5 − (54.5−42)/115) = 50% − 0.10689·L
     (logo.svg: viewBox "8 42 421 115", circle cx 289.5 cy 54.5 r 12.5) */
  transform-origin:
    calc(50% + 0.16865 * var(--logo))
    calc(50% - 0.10689 * var(--logo));

  animation: intro-open 2s var(--ease-in-out, cubic-bezier(.65,.05,.36,1)) both;
}

/* Hold on the wordmark, then open. No opacity anywhere in here — the
   sheet is removed by the hole growing past the frame, not by dissolving.
   That puts the whole job on the mask, and a mask that grows around dead
   centre never finishes it: the centre of "Nestify" falls in the gap
   between the s and the t, so blowing it up just enlarges that gap and
   you end on a full screen of cream.

   So the whole thing blooms out of the dot on the i.

   🔴 It grows by SCALING THE ELEMENT, not by growing mask-size. Animating
   mask-size while nudging mask-position toward the dot looks obvious and
   does not work: the position percentage resolves against
   (box − image), so as the image explodes the anchor swings right off the
   canvas and drifts back. Measured on 1440×900 — dot at (825, 384) during
   the hold, then (4470, −2008) at 1300ms, (3535, −1504) at 1500ms, only
   back in frame for the last 300ms. Middle of the wipe was a blank cream
   screen. A transform pinned to `transform-origin` has no such coupling:
   the dot stays exactly where it was in the held wordmark.

   Scale is set by the dot, not picked by eye. Its on-screen radius during
   the hold is only L × 12.5/421 ≈ 18px at L=620, and the far corner is
   ~973px away on 1440×900, ~1592px on 2560×1440, ~2326px on 3840×2160 —
   so ≥127× is needed at the top end. 160 leaves headroom. */
@keyframes intro-open {
  0%,
  35%   { transform: none; }
  100%  { transform: scale(160); }
}
/* belt and braces: once the timeline is done the sheet is gone outright,
   so nothing can be left sitting over the video */
.intro.is-open .intro__cover { display: none; }

/* photography needs a foot to sit the type on — weighted to the
   bottom edge only, not a flat wash over the picture */
/* bottom left. The gutter is the header's own — max(5vw, 32px) — so the
   slogan's left edge lines up with the wordmark above it. The 11svh floor
   is not slack: the drain drop has to sit under the last line and still be
   a whole circle rather than one clipped by the frame. */
/* ---------- scene two ----------
   Laid out from the first frame and simply uncovered as the film shrinks
   off it — the tiles are never "brought in", they were always there. */
.intro__copy {
  position: absolute; z-index: 1;
  left: 0; right: 0; top: 50%;
  /* the -50% is the centring, the rest is the arrival — one transform, so
     the two can never fight over the property */
  transform: translateY(calc(-50% + (1 - var(--lit, 0)) * 16px));
  opacity: var(--lit, 0);
  padding: 0 max(5vw, 32px);
  /* centred — the film lands INSIDE this sentence, so the block reads as
     one object on the screen rather than as copy set against an edge */
  text-align: center;
}


/* the corner, as a share of the card's own width — declared once here
   because intro.js lands the film on the SAME number. The film ends up
   sitting in this grid; if the two corners disagree by even a couple of
   pixels it reads as the odd one out. */
.intro {
  --tile-r: .14;
  /* the film's resting width, as a share of the stage. It is the one
     rounded rect among the marks, so it does not need to equal any tile —
     it needs to sit in the same size family as them. */
  --film-slot: 13%;
}


/* ---------- scene one's line ----------
   Over the film, dead centre, and in the display serif. It is typed out
   once the opener has finished and then leaves on the scroll, so it never
   shares the screen with scene two's copy. */
/* bottom left, ranged left. The gutter is max(5vw, 32px) — the header's
   own — so the headline's stem lines up with the wordmark above it rather
   than merely being "near the edge".

   🔴 No translateY here any more. intro.js writes this transform every
   frame to lift the block as it leaves, and while the block was centred it
   had to carry the -50% centring in that same value. Anchored to the
   bottom there is nothing to centre, so the JS writes a plain translate —
   leave both alone or they will fight over the property. */
/* ---------- scene one's block, after zipline.com ----------
   Two anchors, not one: the name sits at 43.4% of the height and the
   sentence and the button sit at the foot. Measured off zipline at
   1440×900 — headline centre y=390 (43.4%), CTA bottom 68px up (7.6svh),
   everything ranged left off one gutter.

   The container is the whole stage so intro.js can lift and fade it as a
   single thing on the scroll; the two blocks inside it carry the positions.

   🔴 The gutter is on the BLOCKS, not on the container. Padding on the
   container does nothing for them: an absolutely positioned child resolves
   against its ancestor's PADDING BOX, and the padding box *includes* the
   padding — only in-flow children get inset by it. Set it once on the
   container and both blocks sit flush against the screen edge at x=0
   (measured, first attempt). */
/* 🔴 LEFT, and it has to be set HERE. All three beats — headline, sub and
   the pill — inherit their alignment from this one box, so a `center` here
   silently re-centres the lot even though each of their own boxes already
   starts at the gutter. That is what it was doing: every box at 72, every
   piece of content somewhere else (headline ink at 465, sub at 256, pill
   at 652). One property, three symptoms.

   The gutter is max(5vw, 32px) — the header's own, so the column runs
   straight down from the wordmark. See the ink note on .intro__hero-line
   for why the headline needs a nudge on top of that and the other two
   don't. */
.intro__hero {
  position: absolute; inset: 0; z-index: 2;
  text-align: left;
  pointer-events: none;
}
.intro__hero-line,
.intro__hero-foot {
  position: absolute;
  left: max(5vw, 32px); right: max(5vw, 32px);
}
.intro__hero-line { top: 43.4%; transform: translateY(-50%); }
.intro__hero-foot { bottom: 7.6svh; }
/* ---------- scene one's block, proportioned off labs.google ----------
   Measured on their featured hero at 1440×900, all three beats:

     headline      120px  w400  line-height 1.15
     sub            24px  w300              0.20 of the headline
     button label   21px  w500              0.175, in a 65px pill
     gap head→sub   15px      gap sub→button 40px

   120px at 1440 is 8.3vw, which is what the headline takes here now — it
   was 6vw, and the block read as a caption on a video rather than as the
   first thing on the page. The pill was already built to their spec (see
   .intro__cta below: 65px, 100px corner, 21px/500, transparent).

   🔴 ONE deliberate departure: the sub stays at ~0.27, not their 0.20.
   Their line is a caption under a product NAME; ours is a claim answered
   by a second claim, and at a fifth it reads as a footnote to the title
   instead of the other half of the thought. Their w300 goes the same way
   — see the weight note below. */
.intro__hero-line {
  font-family: var(--font--primary);
  font-size: clamp(28px, 5.4vw, 78px);
  line-height: 1;         /* display leading: 1.05 opens a corridor at 96px */
  font-weight: 600;
  letter-spacing: -.02em;
  margin: 0;
  /* ---------- pay back the P's side bearing ----------
     🔴 Aligning the BOX is not aligning the LETTER. Every glyph carries
     blank space to the left of its ink, so with all three beats anchored
     at the gutter the measured ink still came out: logo 72.5, sub 72.5,
     pill 72.0 — and the headline 78.3. Six pixels adrift, at 78px type,
     directly under the wordmark, where the eye reads the column edge off
     the two of them together.

     🔴 margin-left, NOT text-indent. text-indent INHERITS, and typeHero()
     splits this line into per-character blocks — each inherited the indent
     and the nudge landed twice (measured -12.4px against the -6.24px it
     should be). A margin does not inherit and is applied once, to the box.

     -.08em, not -6px, because a bearing is a fixed share of the type size:
     measured 6.26px at 78px, and 6.26/78 = .080. One value therefore holds
     the whole clamp — at 28px on a phone it resolves to the 2.25px that
     size actually needs.

     ⚠️ This number belongs to the letter P. Change the first character of
     the headline and it has to be re-measured; the sub needs none at all
     because its T only carries .48px. */
  margin-left: -.08em;
  color: #fff;
  /* held until the typing starts — hidden here so the first painted frame
     is already empty; from a script it would flash the finished line */
  visibility: hidden;
}
.intro.is-typing .intro__hero-line { visibility: visible; }

/* ---------- the small line under it, after labs.google ----------
   Their hero is three beats stacked: the name at 120px, one sentence at
   24px/400 saying what it is, then the button. The sentence is the beat
   we were missing — the headline was landing straight onto a CTA with
   nothing in between explaining what the thing does.

   It is body copy, not display: --font--body and a normal weight, at
   roughly a quarter of the headline. Making it a small version of the
   headline instead would read as a second title and the hierarchy would
   flatten. Slightly off pure white so it sits behind the headline in the
   stack rather than beside it — on moving footage that is the difference
   between two lines and one block. */
.intro__hero-sub {
  font-family: var(--font--body);
  /* about a third of the headline. labs.google runs 24 under 120 — a fifth
     — but their headline is a product NAME and the line under it is a
     caption; ours is a claim answered by a second claim, and at a fifth it
     read as a footnote to the title rather than the other half of the
     thought.

     🔴 46ch is a WRAP now, not a hold. The line used to be short enough
     to sit on one at desktop; it is two authored lines — three claims
     across them: it runs the day, it grows with the kids, it looks
     after the parents. The break is placed for the RAG, not at a
     comma: it falls after "grows", so the preposition travels with the
     phrase it belongs to and the two lines still come out near enough
     the same length — which a break at the nearest comma does not. The max-width is what stops a wide
     window undoing both lines. */
  font-size: clamp(15px, 1.5vw, 22px);
  line-height: 1.3;
  /* 🔴 500, not 400. The headline above it is 400 at 86px, where a thin
     stroke still has mass; the same weight at 32px over moving footage has
     none, and white-on-video eats the thin end of a stroke before it eats
     the thick end. The rule is not "one weight for the page" — it is one
     APPARENT weight, and holding that across a 2.7× size drop costs a step
     up. Google Sans is variable 400–700, so this is a real instance, not a
     faux-bold. */
  font-weight: 500;
  letter-spacing: -.01em;
  color: rgba(255, 255, 255, .92);
  /* ranged left with the headline — `auto` here would re-centre it */
  margin: clamp(.9rem, 1.8vw, 1.6rem) 0 0;
  max-width: 46ch;
  /* held for the timeline, same as the button below it */
  opacity: 0; transform: translateY(12px);
  transition: opacity .7s ease, transform .7s var(--ease-out-expo);
}
.intro.is-sub .intro__hero-sub { opacity: 1; transform: none; }

/* ---------- the CTA, after labs.google's "liquid button" ----------
   Their spec, read off the DOM rather than eyeballed: 65px tall, 100px
   corner, background fully TRANSPARENT — the pill is an outline, not a
   fill. Label 21px / 500. On hover an `::after` grows from scale(.1) to
   scale(1) and the label inverts.

   So: an empty ring at rest, and hovering pours it full. The one change
   from theirs is the resting ink — they sit on near-black and can use
   rgb(50,50,50); this sits on moving footage, so it needs white. */
/* 🔴 Everything here is in `em`, off ONE clamped font-size — so the pill
   is a single relative number, not five fixed ones. The ratios are the
   desktop values divided by the 19px they were drawn at: height 54/19 =
   2.85 and padding 34/19 = 1.79 — both since tightened, to 2.35 and 1.3,
   in that order: the height was what read wrong, and the original width
   only looked slack once the pill was shorter. gap 14/19 = .74, opened to
   .96 — the arrow is a diagonal, so its ink starts further from the label
   than a vertical glyph's would and the nominal gap reads tighter than it
   measures. The pill now shrinks with
   the headline instead of staying chunky next to it on a narrow screen.

   The 16px floor is a touch target, not a taste call: 16 × 2.85 = 45.6px
   tall, just over the 44px minimum. Below that the button would be the
   one thing on the screen you cannot reliably hit. */
/* ---------- it arrives as a BALL and opens into the pill ----------
   🔴 The opening is a CLIP, not a width. The pill's width is its content's
   — `inline-flex`, nowrap, two paddings — so animating `width` means
   animating layout, and there is no keyframe on that path that is not a
   reflow. So the pill is laid out at full size from the first frame and
   simply isn't all shown yet: clip-path holds a circle at its left end and
   then opens rightward. Nothing moves; a window widens over something
   already in place. The label never re-wraps, and the corner stays exactly
   the corner the pill was drawn with instead of a radius being stretched.

   🔴 The circle needs no JS and no magic number: inset()'s right edge is
   `100% - the pill's own height`, and `round 100px` clamps to half the box,
   so at ball width the clip IS a circle at every viewport the clamp visits.
   --cta-ball is that height, declared once and read by both.

   🔴 The blue is --bead-blue, which is the logo's i-dot — the same ink the
   intro's mark flies in as and the same one this button's hover fill pours
   in (--btn-disc, below). So the ball that opens the button and the ball
   that fills it are one colour, not two that happen to look alike. */
.intro__cta {
  /* the shape is .btn-ball, in styles.css — this is only what is the
     intro's own: where it sits, and the blue it fills with on hover */
  margin-top: clamp(1.75rem, 3.5vw, 3rem);
  pointer-events: auto;
}
/* the cue: intro.js adds .is-cta to the section, which opens the ball —
   the open state itself is .btn-ball.is-open in styles.css */
.intro.is-cta .intro__cta { opacity: 1; scale: 1; background: var(--nes-cream); clip-path: inset(0 0 0 0 round 100px); }

/* the label waits for the pill to have somewhere to put it, then settles
   rightward — with the opening, not against it */
.intro__cta .btn-label {
  opacity: 0; translate: -.5em 0;
  transition: opacity .3s ease .38s, translate .52s var(--ease-out-expo) .38s;
}
.intro.is-cta .intro__cta .btn-label { opacity: 1; translate: 0; }

/* the hover is .btn-disc's, shared with the header and the hub button —
   see the block at the foot of styles.css. --btn-disc is the bead blue,
   the same ink the logo's dot carries, so the cream pill fills with the
   blue already on screen. */
.intro__cta { --btn-disc: var(--bead-blue); }
.intro__cta:hover { color: #fff; }

/* 🔴 NO ARROW ON THIS ONE, and it is the only button on the site without.
   The shared -45° arrow means "this leaves for somewhere" — the header's
   pills, the privacy card's "Learn more" — and this button doesn't leave,
   it scrolls. Turning it 90° said the right thing and looked bolted on: a
   down arrow inside a pill reads as a dropdown or a download, and the pill
   is already sitting at the bottom of the last frame with the whole page
   under it. The position is the cue. The word carries the rest. */

/* ---------- scene two's copy ----------
   The same voice as scene one — "The parent copilot" in the display serif,
   over the film — carried onto the page. The two scenes are one sentence
   continuing, so nothing here changes typeface mid-thought.

   The three lines come first and the payoff sits UNDER them, so the block
   reads top-down in the order it is animated. */
.intro__steps {
  font-family: var(--font--primary);
  /* no caps, no tracking: read out one at a time, these are sentences, and
     spaced-out small caps would make them labels instead */
  font-size: clamp(17px, 1.6vw, 25px);
  font-weight: 400;
  letter-spacing: 0;
  margin: 0;
  color: var(--nes-ink);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: clamp(.6rem, 1.6vw, 1.6rem);
}
.intro__lead {
  font-family: var(--font--primary);
  transition: opacity 0.5s ease;

/* 🔴 It is the only thing on the screen now, so it carries it. The size
     is capped off the VIEWPORT WIDTH rather than a fixed px ceiling
     because it may not wrap — at 6vw the whole sentence plus the film
     measures ~12.6em, which clears a 1440 stage with its gutters and is
     still the largest it can be without touching them. */
  /* As large as this copy can be and still hold its two authored lines.
     🔴 NOT double scene one's 5.4vw. Doubling needs 10.8vw, and at that
     size "and Nestify runs the day." breaks in half — the block renders as
     four lines with the film stranded on its own. Binary-searched the
     largest size that still yields exactly two rendered rows: 8.06vw,
     identical at 1024, 1440 and 2010, so it is the copy that sets the
     ceiling, not the viewport. 7.9 keeps a little in hand for a fallback
     font's wider metrics. That is 1.46× scene one, not 2×. */
  font-size: clamp(40px, 9.5vw, 170px);
  /* two authored lines, broken where the sentence breaks — so no nowrap.
     The leading is open rather than tight: with the film sitting inside
     line two, .94 pulled the rows close enough that the picture crowded
     the line above it. */
  line-height: 1.0;
  /* matched to scene one's line — the two headlines are the same voice at
     two moments, so the weight has to agree as well as the size */
  font-weight: 600;
  letter-spacing: -.02em;
  margin: clamp(.25rem, .6vw, .6rem) auto 0;
  color: var(--nes-ink);
}



/* 🔴 Both lines arrive TOGETHER, as one block. Nothing here is typed,
   staggered or read out item by item — the copy is two lines of one
   thought, and lighting them in sequence made the reader wait on the
   scroll for the second half of a sentence they could already see was
   coming. intro.js writes one --lit on .intro__copy and that is all. */

@media (max-width: 640px) {
  /* three lines in a row is too much at phone widths — stack them */
  .intro__steps { flex-direction: column; gap: .5rem; }
}

/* the bar carries the same wordmark as the sheet, so it stays out of
   the way until the hole has opened past it */
.site-header {
  opacity: 0;                                   /* the keyframe's implicit start */
  animation: intro-fade-in .55s ease 1.6s both;   /* solid before the drop leaves it */
}

@keyframes intro-fade-in { to { opacity: 1 } }
@keyframes intro-rise {
  from { opacity: 0; transform: translateY(26px) }
  to   { opacity: 1; transform: none }
}

/* 🔴 THE OPENER DOES NOT START UNTIL THERE IS A PICTURE BEHIND IT.
   The sheet is cream and the section behind it is cream, so a wordmark
   punched over a video that has not decoded yet is cream on cream —
   nothing in the hole to look at. These animations are on a fixed clock
   from page load, so a heavy file means the hold AND the wipe both play
   against a blank screen and the opener is over by the time the picture
   lands. (Measured on the 10.7MB placeholder: first frame ~3.5s, sheet
   finished at 2.0s. The opener was not missing — it had already been
   spent.) intro.js flips `is-intro-go` on the first video frame, the
   poster, or a 2s cap, whichever comes first, and all three timelines
   start together from there. */
.intro__cover,
.site-header { animation-play-state: paused; }
html.is-intro-go .intro__cover,
html.is-intro-go .site-header { animation-play-state: running; }

/* no mobile override: the header keeps max(5vw, 32px) at every width,
   and the slogan has to keep matching it */

/* skip straight to the open state */
@media (prefers-reduced-motion: reduce) {
  .intro__cover { animation: none; display: none; }
  .intro__hero-line,
  .site-header { animation-duration: .01s; animation-delay: 0s; }
  /* intro.js's showAll() adds every class at once on this path, so without
     this the ball-to-pill would still play out in full — the one thing the
     setting is asking not to happen */
  .intro__cta,
  .intro__cta .btn-label { transition-duration: .01s; transition-delay: 0s; }
}



/* ---------- the slot the film lands in ---------- */
/* sized off the headline's own em, so it scales with the type at every
   viewport: 1.1em tall, 16:10, and a hair of air either side */
.intro__slot {
  display: inline-block;
  vertical-align: 0em;          /* sits on the text baseline */
  height: 0.74em;
  aspect-ratio: 16 / 10;
  /* 🔴 No horizontal margin. The slot already sits between two real space
     characters, so a margin lands ON TOP of them: measured at 86px, a word
     space is 18.33px and the film had 18.33 + 13 = 31.3px either side —
     1.7× every other gap in the sentence, which is what read as broken
     tracking. With the margin gone it is spaced exactly like a word, and
     it tracks the font's own space width at every size instead of a
     hard-coded em. */
  border-radius: var(--film-r);
  /* 🔴 No background. The film is scaled onto this box, so its edge is a
     row of antialiased half-transparent pixels — anything painted
     underneath shows through them as a halo all the way round. A 12%
     placeholder grey read as a grey rim on the finished shot.
     And the corner is the SAME variable the film lands on: a slot at .16em
     under a film at 24px meant the slot's corner poked out past the
     picture's at every size. */
  background: none;
}

/* the line under the headline: same voice as scene one's sub, and a
   tighter leading than body copy would take — it is three short lines
   under a display line, not a paragraph to read at length */
.intro__sub {
  font-family: var(--font--body);
  font-size: clamp(15px, 1.5vw, 22px);
  line-height: 1.32;
  font-weight: 600;
  letter-spacing: -.005em;
  /* a measure that lands it on three lines, and `balance` to even them —
   narrowing alone just moves where the greedy break falls */
  max-width: 52ch;
  text-wrap: balance;
  margin: clamp(1.2rem, 2.2vw, 2.2rem) auto 0;
  color: var(--nes-ink);
  opacity: .88;
}

/* 🔴 A phone cannot hold this copy on two lines at the desktop ratio, so
   the ratio itself drops on narrow screens — but 5.8vw was cut for the
   OLD copy ("and Nestify runs the day.", 425px of type against 366px of
   usable width). The line is two authored rows now and the longest of
   them, "Time ▭ to just", measures 6.62em. At 390 that made the sentence
   22.6px tall and 150px wide inside a 326px column — it read as a caption
   under a screen of empty cream, not as the one thing on the page.

   11.5vw fills that column the way the desktop line fills its own: 45px
   at 390, 297px of type in 326px of width, ~91% at every phone size. The
   68px ceiling is what keeps the breakpoint invisible — it is reached at
   591px and desktop's 9.5vw is 66.5px at 701, so nothing jumps when the
   query flips. */
@media (max-width: 700px) {
  .intro__lead { font-size: clamp(30px, 11.5vw, 68px); }
  /* the corner keeps its share of the type: .118 × the lead, as above */
  :root { --film-r: clamp(3.5px, 1.36vw, 8px); }
}





/* ============================================================
   THE MARK — one element, two shapes.

   In the sentence it is a marker stroke over the word that turns
   (parent / child / family) — as tall as the word itself, standing on its
   baseline, with the type reading through it. On the way out it becomes the
   clock's ball: width, height and corner are interpolated together, so the
   line rounds itself into a circle rather than cross-fading into one.

   🔴 Fixed, and a sibling of every section — not a child of the copy. It
   has to survive the intro's sticky stage releasing, because the thing it
   turns into lives in the next section entirely.

   🔴 Sized in px from JS rather than scaled with a transform: a transform
   would stretch the corner radius with the box, and a 3px rule blown up to
   a 60px ball would carry a 60px-wide flattened corner the whole way. */
/* the word in flight between scene two's sentence and the deck's headline.
   Page-level and fixed, because both of the boxes it travels between sit
   inside overflow:hidden stages — see the note on `carrier` in intro.js.
   Everything about it is written per frame from the two measured ends; the
   only thing declared here is that it is out of the flow and out of the
   way. */
.intro__carry {
  position: fixed; z-index: 4;
  font-family: var(--font--primary);
  font-weight: 500;
  line-height: 1;
  font-style: normal;
  white-space: pre;
  opacity: 0;
  pointer-events: none;
}

/* ---------- the flip card ----------
   A fixed-position card that bridges the intro tile to hero slide 1.
   perspective lives on the wrapper so it applies to flip__inner's
   rotateY; preserve-3d makes the two faces exist in shared 3D space;
   backface-visibility: hidden makes each face invisible when it is
   rotated away from the camera. */
/* ---------- the copy the bridge carries ----------
   A fixed clone of the hero's first line, parked where that line will be
   once the hero stage pins. See the note in intro.js for why the real one
   cannot do this job: it is a viewport further down the page.

   🔴 Everything in here is SCRUBBED off --wipe-n, with the transitions
   switched off. The real copy uses a timed transition, which is right when
   a slide change fires it; here the wipe IS the flip, so it has to be tied
   to the scroll frame by frame or the two gestures drift apart the moment
   anyone scrolls slowly.

   🔴 Same z-index band as the flipper — it is the other half of the same
   bridge, and it has to clear the intro's own sticky stage (z-index 2). */
.intro__bridge {
  pointer-events: none;
  color: var(--nes-ink);
  opacity: 0;
}
/* 🔴 TWO classes, and it has to be two. The clone keeps `hero__content` on
   purpose — that is where its column layout, padding and type come from, so
   the words sit exactly where the real ones will. But hero.css loads AFTER
   intro.css and gives that same single class `position: relative; z-index: 3`,
   so a one-class selector here LOSES the tie: the clone stopped being fixed,
   fell back into flow as the last block of <body>, and parked itself at
   y ≈ 24084 — a mile below the fold, which is why the bridge read as
   "measured wrong" when the measurement (left 763.2, top 0) was correct all
   along. Matching both classes wins on specificity and keeps the parking
   spot. Anything hero.css also sets on .hero__content belongs in HERE. */
.intro__bridge.hero__content {
  position: fixed;
  z-index: 500;
}
.intro__bridge .hero__mask > span {
  clip-path: inset(0 calc(100% - var(--wipe-n, 0) * 100%) 0 0);
  translate: calc(-.35em + var(--wipe-n, 0) * .35em) 0;
  transition: none;
}
.intro__bridge .hero__eyebrow {
  opacity: calc(.62 * var(--wipe-n, 0));
  transform: none;
  transition: none;
}

.intro__flipper {
  position: fixed;
  z-index: 500;
  opacity: 0;
  pointer-events: none;
  /* perspective: distance from viewer to the card plane. 1000px gives
     a natural-looking foreshortening without the extreme fish-eye of
     a very short value. */
  perspective: 1000px;
  perspective-origin: 50% 50%;
}
.flip__inner {
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  will-change: transform;
}
.flip__front,
.flip__back {
  position: absolute;
  inset: 0;
  overflow: hidden;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
/* back face starts pre-rotated 180° so it is invisible until the
   inner's rotateY reaches 90°, at which point the faces swap */
.flip__back {
  transform: rotateY(180deg);
}
/* the front is still the intro's film; the back is slide one's still */
.flip__front video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ---------- the picture does not move while the card turns ----------
   🔴 NOT object-fit: cover, AND THAT IS THE WHOLE POINT. The card's box is
   interpolated every frame from the intro tile to the hero slot — 488×305 to
   720×720 at 1440×900 — so its ASPECT travels from 1.60 to 1.00. `cover`
   re-derives the crop from the box aspect on every one of those frames:
   which edge binds the scale flips partway through, so the picture zooms and
   re-crops continuously and the composition drifts around inside the card
   for the whole flip. That is the "画面弹来弹去".

   Binding the scale to WIDTH ALONE takes the box's height out of the
   calculation entirely. The image is centred on the card and grows with it,
   and the changing height simply reveals more of a picture that has not
   moved — a frame opening, not a photograph being panned. One centred crop,
   from the turn to the landing.

   🔴 IT STILL COVERS, and that is arithmetic rather than luck. The four hero
   stills are portrait, 0.67–0.71, so at width:100% the rendered height is
   1.41–1.49× the card's width, and the card is never taller than it is wide
   (it ends square). The shortfall case cannot arise.

   ⚠️ A LANDSCAPE HERO IMAGE WOULD BREAK THIS. Ratio > 1 means the rendered
   height is LESS than the width and the card would letterbox top and bottom
   where the slide behind it still covers. If a wide picture ever joins the
   deck, this rule and .hero__inner's background-size have to be decided
   together — see the note there.

   🔴 AND IT MATCHES THE SLIDE AT THE LANDING, which is the one frame where
   the two must agree exactly. .hero__inner is always square and paints its
   picture with background-size: cover, centred; for a portrait image in a
   square box cover binds on width too, so both land on the identical scale
   and the identical centre. The swap is two copies of one crop. */
.flip__back img {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 100%;
  height: auto;
  display: block;
}
