/* ==========================================================================
   SocialBrag motion layer, the CSS half. Load after components.css.
   Only for builds that load motion.js. See README.md in this folder.
   ========================================================================== */

/* The same timing system as motion.js, for CSS transitions: a hover and a
   scroll reveal should feel like one hand made them. power3.out in GSAP is
   exactly cubic-bezier(.25,1,.5,1). A build that uses this layer points the
   library's own --ease at it in tokens.css. */
:root{
  --m-ease:        cubic-bezier(.25,1,.5,1);
  --m-ease-in-out: cubic-bezier(.76,0,.24,1);
  --m-s:           .4s;
  --m-m:           .8s;
  --m-l:           1.2s;
}

/* Lenis. It scrolls the real document, so position:sticky keeps working,
   but the library's scroll-behavior:smooth must stand down while it runs or
   the two fight over every anchor jump. */
html.lenis, html.lenis body{height:auto;}
html.lenis-smooth{scroll-behavior:auto !important;}
html.lenis-smooth [data-lenis-prevent]{overscroll-behavior:contain;}
html.lenis-stopped{overflow:clip;}

/* "lines" masks clip each line to its box, which cuts the tails off g, y
   and p at tight display line-heights (a checklist item, qa-checklist 4).
   The padding widens the clip and the negative margin gives the space back. */
.m-line-mask{padding-bottom:.16em;margin-bottom:-.16em;}

/* "media": the frame clips, the picture moves. The frame needs a size of
   its own (an aspect-ratio, or a parent that sets one). The starting scale
   matches where motion.js picks it up, so the first frame never jumps. */
[data-m="media"]{overflow:hidden;position:relative;}
[data-m="media"] img,
[data-m="media"] video{
  display:block;width:100%;height:100%;object-fit:cover;
}
@media (prefers-reduced-motion:no-preference){
  [data-m="media"] img,
  [data-m="media"] video{transform:scale(1.14);will-change:transform;}
}

/* "sideways": a row of cards. Everywhere by default, and on phones always,
   it is a native swipe row that snaps card by card. On a wide screen with
   motion on, motion.js pins the section and adds .m-sideways, and the row
   stops scrolling itself and is moved instead, so its own overflow must
   stop clipping and the section clips instead. */
[data-m="sideways"] [data-m-row]{
  display:flex;
  overflow-x:auto;overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory;
  scrollbar-width:thin;
}
[data-m="sideways"] [data-m-row] > *{scroll-snap-align:start;flex:none;}
.m-sideways{overflow:hidden;}
.m-sideways [data-m-row]{overflow:visible;scroll-snap-type:none;will-change:transform;}

/* Stroke drawings keep their joins round, so a line being drawn does not
   show a square end cap marching along it. */
[data-m="draw"] *{stroke-linecap:round;stroke-linejoin:round;}

/* "expand", "shrink" and "wall": the section becomes a run of scroll and its stage
   holds still inside it. position:sticky, never wheel capture, so the page
   always scrolls normally. motion.js sets --m-run (screens of scroll) and
   adds the class; without it both are ordinary sections. clip, not hidden:
   hidden would make the section a scroll box and sticky would stop working.
   lvh, so on a phone the stage still fills the screen when the address bar
   slides away. */
.m-expand,.m-wall{height:calc((1 + var(--m-run, 1.2)) * 100lvh);overflow:clip;}
.m-expand [data-m-stage],.m-wall [data-m-stage]{position:sticky;top:0;height:100lvh;overflow:clip;}
/* "shrink" holds its stage under the page's sticky header instead of under
   the top of the screen: --m-top is that header's height (0 without one).
   The run is still data-m-run screens of scroll. */
.m-shrink{height:calc((1 + var(--m-run, 1)) * 100lvh - var(--m-top, 0px));overflow:clip;}
.m-shrink [data-m-stage]{position:sticky;top:var(--m-top, 0px);height:calc(100lvh - var(--m-top, 0px));overflow:clip;}

/* The growing picture fills whatever box it sits in, and that box keeps
   its size while the picture leaves it. Its starting scale matches where
   motion.js picks it up. */
[data-m-grow]{position:absolute;inset:0;overflow:hidden;z-index:2;}
[data-m-grow] img,[data-m-grow] video{display:block;width:100%;height:100%;object-fit:cover;}
/* expand's picture starts a touch large and settles as it grows; shrink's
   starts at its natural size, because its first frame is the full photo. */
@media (prefers-reduced-motion:no-preference){
  [data-m="expand"] [data-m-grow] img,[data-m="expand"] [data-m-grow] video{transform:scale(1.1);}
}
[data-m-fly],[data-m-part]{will-change:transform;}
[data-m="expand"] [data-m-after]{visibility:hidden;}
.m-expand [data-m-after]{visibility:visible;}

/* "film": the stage is one screen whether the move runs or not, so nothing
   on screen moves when it starts; only the section grows, below the fold.
   The poster and the canvas fill the stage underneath the words: the stage
   isolates, and both sit at z-index -1 inside it, so the page's words need
   no positioning of their own. The canvas shows only once it has painted a
   frame. lvh, as above: on a phone the bottom of the stage is under the
   browser's toolbar until the first scroll, so keep words out of it. */
[data-m="film"] [data-m-stage]{position:relative;height:calc(100lvh - var(--m-top, 0px));overflow:clip;isolation:isolate;}
[data-m="film"] [data-m-film],.m-film-canvas{position:absolute;inset:0;width:100%;height:100%;z-index:-1;}
[data-m="film"] [data-m-film]{display:block;object-fit:cover;}
.m-film-canvas{opacity:0;pointer-events:none;}
.m-film-canvas.m-live{opacity:1;}
.m-film{height:calc((1 + var(--m-run, 2)) * 100lvh - var(--m-top, 0px));overflow:clip;}
.m-film [data-m-stage]{position:sticky;top:var(--m-top, 0px);}
[data-m="film"] [data-m-after]{visibility:hidden;}
.m-film [data-m-after]{visibility:visible;}

/* "ring": panels on a ring that turns with the scroll. Without the move
   (a phone, a tablet, reduced motion) its list is a swipe row, and a grid
   on a wide screen; a page may restyle either. With it, the section is a
   run of scroll and the stage holds still under --m-top, as in film. The
   list becomes the ring: every panel stacked in its middle, turned and
   pushed out by motion.js. Its height and the panels' size are the page's,
   set under .m-ring; the ring's radius follows the panels' width. Backs
   hidden, so a turned-away panel never shows its text mirrored. */
[data-m="ring"] [data-m-ring]{
  display:flex;gap:1rem;
  overflow-x:auto;overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory;scrollbar-width:thin;
}
[data-m="ring"] [data-m-ring] > *{flex:none;scroll-snap-align:start;}
@media (min-width:1024px){
  [data-m="ring"] [data-m-ring]{display:grid;grid-template-columns:repeat(auto-fill,minmax(15rem,1fr));overflow:visible;}
}
.m-ring{height:calc((1 + var(--m-run, 3)) * 100lvh - var(--m-top, 0px));overflow:clip;}
.m-ring [data-m-stage]{position:sticky;top:var(--m-top, 0px);height:calc(100lvh - var(--m-top, 0px));overflow:clip;}
.m-ring [data-m-ring]{display:block;position:relative;overflow:visible;transform-style:preserve-3d;will-change:transform;}
.m-ring [data-m-ring] > *{position:absolute;inset:0;margin:auto;backface-visibility:hidden;-webkit-backface-visibility:hidden;}

/* "flip": a panel with two faces that turns over with the scroll. Without
   the move (a phone, a tablet, reduced motion) both faces lie side by side,
   flat, so nothing on either is lost; a page may restyle or hide one. With
   it, the section is a run of scroll, the stage holds still under --m-top as
   in film, and the faces stack in one place with the back turned away. The
   panel's size and look are the page's. */
[data-m="flip"] [data-m-flip]{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,16rem),1fr));gap:1.5rem;}
.m-flip{height:calc((1 + var(--m-run, 1.2)) * 100lvh - var(--m-top, 0px));overflow:clip;}
.m-flip [data-m-stage]{position:sticky;top:var(--m-top, 0px);height:calc(100lvh - var(--m-top, 0px));overflow:clip;}
.m-flip [data-m-flip]{grid-template-columns:1fr;gap:0;transform-style:preserve-3d;will-change:transform;}
.m-flip [data-m-front],.m-flip [data-m-back]{grid-area:1/1;backface-visibility:hidden;-webkit-backface-visibility:hidden;}
.m-flip [data-m-back]{transform:rotateY(180deg);}

/* "wall": its floating pictures exist only while the scene runs. */
[data-m="wall"] [data-m-float]{display:none;}
.m-wall [data-m-float]{display:block;position:absolute;top:0;pointer-events:none;will-change:transform;}

/* "reveal": the frame clips, the picture moves, as with "media". */
[data-m="reveal"]{overflow:hidden;position:relative;}
[data-m="reveal"] img,[data-m="reveal"] video{display:block;width:100%;height:100%;object-fit:cover;}
@media (prefers-reduced-motion:no-preference){
  [data-m="reveal"] img,[data-m="reveal"] video{transform:scale(1.3);will-change:transform;}
}
