/* ─────────────────────────────────────────────────────────────
   Scroll-driven UI film.

   Apple's product-page technique: a tall track scrolls, a stage stays
   pinned inside it, and UI elements animate against the track's progress
   rather than firing on entry. Scrolling back rewinds the whole scene.

   No people, no footage, no video file — the entire thing is DOM nodes
   moving. Total cost is this stylesheet; nothing to download.

   Five beats, keyed to the track:
     0–22%   messages arrive from every side, from every kind of business
     22–45%  they collapse into one ordered inbox
     45–62%  the one that matters ignites and rises
     62–84%  a draft builds under it, dashed — nothing sent yet
     84–100% a person sends; dashes turn solid

   IMPORTANT — the no-animation state is the FINAL state.
   Browsers without scroll-driven CSS, and anyone on reduced motion, get
   the composed end frame: a sorted inbox with a sent reply. The scene
   reads correctly standing still, so the motion is decoration, never the
   only carrier of meaning.
   ───────────────────────────────────────────────────────────── */

.film{position:relative;background:var(--paper-deep);border-top:1px solid var(--hair)}
.film__track{position:relative;height:340vh}
.film__stage{
  position:sticky;top:0;height:100vh;
  display:grid;grid-template-rows:auto 1fr;align-content:center;
  gap:clamp(20px,4vh,44px);padding-block:clamp(56px,9vh,88px);
  overflow:hidden;
}
.film__copy{position:relative;z-index:3;text-align:center}
.film__scene{
  position:relative;width:100%;max-width:420px;margin-inline:auto;
  display:grid;place-items:center;
}

/* ── the caption changes with the beat ── */
.film__cap{position:relative;height:2.6em}
.film__cap span{
  position:absolute;inset:0;opacity:0;
  font-size:clamp(15px,1.6vw,19px);line-height:1.4;color:var(--sub);
}
.film__cap span:last-child{opacity:1}   /* end frame when static */

/* ── incoming messages: one per kind of business ── */
.bub{
  position:absolute;z-index:1;
  padding:9px 13px;border-radius:14px;
  background:var(--paper-raised);border:1px solid var(--hair);
  font-size:13px;line-height:1.35;white-space:nowrap;
  box-shadow:0 12px 28px -22px rgba(20,20,19,.5);
  opacity:0;                              /* gone by the end frame */
}
.bub b{font-weight:600;display:block;font-size:11px;color:var(--faint);
  font-family:var(--mono);letter-spacing:.04em;margin-bottom:2px}

/* ── the inbox they resolve into ── */
.film__card{
  position:relative;z-index:2;width:100%;
  background:var(--paper);border:1px solid var(--hair);border-radius:var(--r-lg);
  box-shadow:0 40px 80px -50px rgba(20,20,19,.42);overflow:hidden;
}
.film__bar{
  display:flex;align-items:center;justify-content:space-between;
  padding:10px 15px;border-bottom:1px solid var(--hair-soft);
  font-family:var(--mono);font-size:11px;color:var(--faint);
}
.film__row{
  display:flex;gap:10px;padding:11px 15px;border-bottom:1px solid var(--hair-soft);
  align-items:flex-start;
}
.film__row:last-of-type{border-bottom:0}
.film__row i{width:3px;border-radius:2px;background:var(--hair);align-self:stretch;flex-shrink:0}
.film__row[data-risk] i{background:var(--signal)}
.film__n{font-size:13px;font-weight:650;letter-spacing:-.005em}
.film__l{margin-top:2px;font-size:12px;color:var(--sub)}
.film__m{margin-top:4px;font-family:var(--mono);font-size:11px;color:var(--sub)}
.film__row[data-risk] .film__m{color:var(--signal)}

/* ── the reply: dashed while it is a draft, solid once sent ── */
.film__draft{
  margin:12px 15px 15px;padding:10px 12px;border-radius:var(--r-sm);
  font-size:13px;line-height:1.45;
  border:1.5px solid var(--ink);background:var(--ink);color:var(--paper);
}
.film__sent{
  margin:-8px 15px 14px;text-align:right;
  font-family:var(--mono);font-size:11px;color:var(--ok);
}

/* ═══ scroll-driven: the actual film ═══ */
@supports (animation-timeline: view()){
  @media (prefers-reduced-motion: no-preference){
    .film{timeline-scope:--film}
    .film__track{view-timeline-name:--film;view-timeline-axis:block}

    /* Every animated part is bound to the track's timeline.
       NOTE: these use animation-name/fill-mode longhands, never the
       `animation:` shorthand. The shorthand resets animation-timeline to
       `auto`, which silently detaches the element from --film and leaves
       it frozen on its end frame — the whole scene renders as a static
       final state with no error anywhere. */
    .film__cap span,.bub,.film__card,.film__row,
    .film__row[data-risk] i,.film__row[data-risk] .film__m,
    .film__draft,.film__sent{
      animation-timeline:--film;
      animation-fill-mode:both;
      animation-timing-function:linear;
      animation-duration:auto;
    }

    /* captions cross-fade through the beats */
    .film__cap span{animation-name:capIn}
    .film__cap span:nth-child(1){animation-range:contain 0%   contain 20%}
    .film__cap span:nth-child(2){animation-range:contain 20%  contain 43%}
    .film__cap span:nth-child(3){animation-range:contain 43%  contain 62%}
    .film__cap span:nth-child(4){animation-range:contain 62%  contain 84%}
    .film__cap span:nth-child(5){animation-range:contain 84%  contain 100%}
    .film__cap span:last-child{opacity:0}

    /* messages fly in from the edges, hold, then collapse inward */
    .bub{animation-name:bubFly;animation-range:contain 0% contain 45%}
    .bub:nth-of-type(1){--x:-150%;--y:-190%;--r:-7deg;animation-delay:0s}
    .bub:nth-of-type(2){--x: 155%;--y:-150%;--r: 6deg}
    .bub:nth-of-type(3){--x:-175%;--y:  30%;--r: 4deg}
    .bub:nth-of-type(4){--x: 170%;--y:  60%;--r:-5deg}
    .bub:nth-of-type(5){--x: -95%;--y: 195%;--r: 6deg}
    .bub:nth-of-type(6){--x: 120%;--y: 205%;--r:-4deg}

    /* the inbox assembles once the messages have converged */
    .film__card{animation-name:cardIn;animation-range:contain 30% contain 52%}
    .film__row{animation-name:rowIn}
    .film__row:nth-of-type(1){animation-range:contain 40% contain 54%}
    .film__row:nth-of-type(2){animation-range:contain 44% contain 58%}
    .film__row:nth-of-type(3){animation-range:contain 48% contain 62%}

    /* risk ignites */
    .film__row[data-risk] i{animation-name:igniteBar;animation-range:contain 52% contain 64%}
    .film__row[data-risk] .film__m{animation-name:igniteText;animation-range:contain 54% contain 66%}

    /* the draft builds dashed, then a person sends and it goes solid */
    .film__draft{animation-name:draftIn;animation-range:contain 62% contain 86%}
    .film__sent{animation-name:sentIn;animation-range:contain 88% contain 100%}
  }
}

@keyframes capIn{
  0%{opacity:0;transform:translateY(6px)}
  18%,82%{opacity:1;transform:none}
  100%{opacity:0;transform:translateY(-6px)}
}
@keyframes bubFly{
  0%  {opacity:0;transform:translate(var(--x),var(--y)) rotate(var(--r)) scale(.86)}
  22% {opacity:1;transform:translate(calc(var(--x)*.62),calc(var(--y)*.62)) rotate(var(--r)) scale(1)}
  62% {opacity:1;transform:translate(calc(var(--x)*.46),calc(var(--y)*.46)) rotate(0deg) scale(1)}
  100%{opacity:0;transform:translate(0,0) rotate(0deg) scale(.72)}
}
@keyframes cardIn{from{opacity:0;transform:translateY(26px) scale(.94)}to{opacity:1;transform:none}}
@keyframes rowIn{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
/* Two keyframes, deliberately. The bar is a 3px rule and animates its
   background; the meta line is text and animates only its colour.
   Sharing one keyframe painted a red block behind the text. */
@keyframes igniteBar{from{background:var(--hair)}to{background:var(--signal)}}
@keyframes igniteText{from{color:var(--sub)}to{color:var(--signal)}}
@keyframes draftIn{
  0%  {opacity:0;transform:translateY(14px);
       border:1.5px dashed rgba(20,20,19,.35);background:#fff;color:var(--ink)}
  55% {opacity:1;transform:none;
       border:1.5px dashed rgba(20,20,19,.35);background:#fff;color:var(--ink)}
  88% {border:1.5px dashed rgba(20,20,19,.35);background:#fff;color:var(--ink)}
  100%{opacity:1;transform:none;
       border:1.5px solid var(--ink);background:var(--ink);color:var(--paper)}
}
@keyframes sentIn{from{opacity:0}to{opacity:1}}

/* ── mobile: shorter track, tighter stage, bubbles pulled inward ── */
@media(max-width:720px){
  .film__track{height:290vh}
  .film__stage{padding-block:40px;gap:16px}
  .film__scene{max-width:min(340px,88vw)}
  .bub{font-size:11.5px;padding:7px 10px;max-width:62vw;white-space:normal}
  @supports (animation-timeline: view()){
    @media (prefers-reduced-motion: no-preference){
      .bub:nth-of-type(1){--x:-58%;--y:-215%}
      .bub:nth-of-type(2){--x: 60%;--y:-170%}
      .bub:nth-of-type(3){--x:-66%;--y: -35%}
      .bub:nth-of-type(4){--x: 64%;--y:  35%}
      .bub:nth-of-type(5){--x:-52%;--y: 205%}
      .bub:nth-of-type(6){--x: 58%;--y: 235%}
    }
  }
}

/* Reduced motion / no support: the composed end frame, already correct. */
@media(prefers-reduced-motion:reduce){
  .film__track{height:auto}
  .film__stage{position:static;height:auto;min-height:0}
  .bub{display:none}
  .film__cap span:last-child{opacity:1}
}

/* Kill switch (see site.css): fall back to the composed end frame. */
html.sda-off .film__track{height:auto}
html.sda-off .film__stage{position:static;height:auto;min-height:0}
html.sda-off .bub{display:none}
html.sda-off .film__cap span{animation:none!important;opacity:0}
html.sda-off .film__cap span:last-child{opacity:1}
html.sda-off .film__card,html.sda-off .film__row,
html.sda-off .film__draft,html.sda-off .film__sent,
html.sda-off .film__row[data-risk] i,
html.sda-off .film__row[data-risk] .film__m{
  animation:none!important;opacity:1!important;transform:none!important;
}
