/* ============================================================================
   Native Apps Team — anim-recall.css
   One looping figure: #anim-recall — "Recall", a conversation with your own
   documents.

   Owns ONLY the <figure class="anim" id="anim-recall"> block in
   assets/anim-recall.html. Every rule is scoped to #anim-recall so this file
   can be linked before OR after assets/anim-name.css without either one
   winning by accident (ID beats the shared `.anim` class selectors).
   Additive: no token name is redefined at :root. No JS. No external requests.

   ── HOW THE LAYOUT WORKS ────────────────────────────────────────────────
   .rc-thread is a fixed-height window (32.8em) with overflow:hidden.
   .rc-scroll is absolutely positioned inside it, pinned to the BOTTOM, and
   holds all six message rows at their final size from the very first frame.
   Nothing is ever inserted, removed or resized — so the figure reserves its
   full height immediately and the page can never shift.

   "Scrolling" is one transform on .rc-scroll. translateY(N em) pushes the
   whole stack DOWN by N; rows that have not arrived yet sit below the fold.
   As each one lands, N shrinks and the whole thread rises together.

   N is chosen to keep the *revealed* part of the thread optically centred in
   the window rather than jammed against its bottom edge — a strictly
   bottom-anchored thread leaves the frame ~85% empty for the first six
   seconds, which reads as a broken box on a marketing page. For a revealed
   block whose bottom sits b em above the stack's bottom:

       N = (b - (V - H)) / 2         V = window 32.8em, H = stack 31.7em

   The window is taller than the stack, so nothing is ever clipped, top or
   bottom, at any point in the loop.

   Every row height is authored in em and every line of bubble copy is its own
   block, so the geometry below is exact at any viewport and any font size.

     row          height   gap    bottom-offset of the row (b)
     R1 Q1         4.00em  0.50   27.7
     R2 A1+chip    6.10em  1.00   21.1
     R3 Q2         4.00em  0.50   16.1
     R4 A2+chip    6.10em  1.00    9.5
     R5 Q3         2.60em  0.50    5.9
     R6 A3         5.40em     —    0.0     stack height H = 31.70em

   The typing pill is position:absolute at each answer row's top-left, so it
   costs no layout and can cross-fade with the answer bubble in place.

   ── HOW THE TIMING WORKS ────────────────────────────────────────────────
   One master period, --rc-loop: 20s. Every element expresses its beats as a
   percentage of that period (1s = 5%) and nothing uses animation-delay for
   sequencing, so the parts can never drift out of step. Three ~6s turns plus
   a ~2s fade-out and reset.

     0.1s  Q1 in      1.2s typing   2.6s A1   3.2s chip
     6.2s  Q2 in      7.2s typing   8.6s A2   9.2s chip
    12.2s  Q3 in     13.2s typing  14.8s A3   (no chip — the honest answer)
    18.6s  thread fades out and drifts up; 20.0s loop restarts

   Questions settle in 0.55s, the typing pill runs ~1.4s (1.6s before the
   third answer — it should feel like it is actually checking), and each chip
   lands 0.6s behind its answer so the citation reads as a separate act.

   The only continuously-running animation is the 1.5s three-dot pulse.

   ── REDUCED MOTION ──────────────────────────────────────────────────────
   Everything outside @media (prefers-reduced-motion: no-preference) already
   draws the FINISHED thread: .rc-scroll sits at translateY(0), all six
   bubbles and both source chips are at opacity 1, and only the typing pills
   are hidden. The animation block only ever ADDS `animation:` declarations,
   so removing motion can never leave a blank or half-built box.

   ── COLOUR ──────────────────────────────────────────────────────────────
   One hardcoded value: #fff on --cobalt (#2B4BFF) for the question bubbles.
   --cobalt is identical in both schemes, so that pair is 5.91:1 in light AND
   dark — comfortably past AA for body text. Everything else is a token.

   Sections
     1  Shell            4  Typing indicator
     2  Thread geometry  5  Static state (= the reduced-motion state)
     3  Bubbles + chips  6  Timeline
   ========================================================================= */


/* ── 1. Shell ──────────────────────────────────────────────────────────── */

#anim-recall{
  /* One master period. Every percentage in section 6 is a slice of this. */
  --rc-loop:20s;
  /* Soft, non-bouncy settle. Matches the token; the literal is only a
     fallback for the case where this file is used without tokens.css. */
  --rc-ease:var(--ease-out,cubic-bezier(.16,1,.3,1));

  box-sizing:border-box;
  max-width:520px;                    /* sibling figures share this cap */
  margin-inline:auto;
  padding:clamp(.75rem,2.5vw,1.25rem);
  background:var(--paper-2);
  border:1px solid var(--hair);
  border-radius:var(--radius);
}

#anim-recall *{ box-sizing:border-box }
#anim-recall p{ margin:0 }

#anim-recall figcaption{
  margin-top:var(--s-1,.6rem);
  font-size:var(--t-cap,.8125rem);
  line-height:1.4;
  color:var(--ink-2);
  text-align:center;
  text-wrap:balance;
}


/* ── 2. Thread geometry ────────────────────────────────────────────────── */

#anim-recall .rc-thread{
  position:relative;
  overflow:hidden;
  max-width:440px;
  margin-inline:auto;

  /* Fluid but tightly capped: 13.5px at 320px, 15px from ~490px up. Bubble
     copy never has to survive a resize, because each line is authored. */
  font-size:clamp(.845rem,.9vw + .66rem,.9375rem);
  line-height:1.4;

  /* Stack is 31.7em; the extra 1.1em is headroom so the first question never
     sits flush against the top and the outro drift has somewhere to go. */
  height:32.8em;
}

#anim-recall .rc-scroll{
  position:absolute;
  left:0; right:0; bottom:0;
  display:flex;
  flex-direction:column;
  /* Resting position of the completed thread — the last value of rcScroll,
     so the static state and the end of the loop are pixel-identical. */
  transform:translateY(-.5em);
}

#anim-recall .rc-row{
  position:relative;                  /* anchor for the typing pill */
  display:flex;
  flex:0 0 auto;                      /* heights below are load-bearing */
}
#anim-recall .rc-row--q{ justify-content:flex-end }
#anim-recall .rc-row--a{ flex-direction:column; align-items:flex-start }

#anim-recall .rc-r1{ height:4em;   margin-bottom:.5em }
#anim-recall .rc-r2{ height:6.1em; margin-bottom:1em  }
#anim-recall .rc-r3{ height:4em;   margin-bottom:.5em }
#anim-recall .rc-r4{ height:6.1em; margin-bottom:1em  }
#anim-recall .rc-r5{ height:2.6em; margin-bottom:.5em }
#anim-recall .rc-r6{ height:5.4em }


/* ── 3. Bubbles + source chips ─────────────────────────────────────────── */

#anim-recall .rc-b{
  max-width:88%;
  /* The 1px steal keeps the border-box exactly n*1.4em + 1.1em tall, which is
     what the row heights in section 2 are built on. Both bubble kinds carry a
     border so both land on the same grid. */
  padding:calc(.55em - 1px) .85em;
  border:1px solid transparent;
  border-radius:1.15em;
  font-size:1em;
  line-height:1.4;
  letter-spacing:-.006em;
  text-align:left;
}

/* #fff on --cobalt = 5.91:1, unchanged between light and dark. */
#anim-recall .rc-b--q{
  background:var(--cobalt);
  color:#fff;
  border-bottom-right-radius:.45em;
}

#anim-recall .rc-b--a{
  background:var(--card);
  color:var(--ink);
  border-color:var(--hair);
  border-bottom-left-radius:.45em;
}

/* Authored line breaks. Keeps every bubble's height deterministic, so the
   scroll offsets stay exact from 320px to 1440px. */
#anim-recall .rc-l{ display:block }

#anim-recall .rc-chip{
  display:inline-flex;
  align-items:center;
  gap:.4em;
  height:1.75em;                      /* em of the THREAD, not of the label */
  margin-top:.4em;
  padding:0 .7em;
  border:1px solid var(--hair);
  border-radius:999px;
  color:var(--ink-2);                 /* 4.66:1 light · 7.53:1 dark */
  white-space:nowrap;
}

/* Sized here rather than on .rc-chip so the pill's padding, gap and height
   stay in thread-em. */
#anim-recall .rc-chip__t{ font-size:.78em; letter-spacing:.005em }

/* Beats the shared `.anim svg{width:100%}` rule in anim-name.css. */
#anim-recall .rc-chip svg{
  display:block;
  flex:0 0 auto;
  width:.9em;
  height:.9em;
  overflow:visible;
}


/* ── 4. Typing indicator ───────────────────────────────────────────────── */

#anim-recall .rc-typing{
  position:absolute;
  top:0; left:0;
  display:flex;
  align-items:center;
  gap:.32em;
  height:2.5em;                       /* == a one-line answer bubble */
  padding:0 .95em;
  background:var(--card);
  border:1px solid var(--hair);
  border-radius:1.15em;
  border-bottom-left-radius:.45em;
  color:var(--ink-2);
  opacity:0;                          /* section 6 raises it; static = off */
  pointer-events:none;
}

#anim-recall .rc-typing i{
  width:.36em;
  height:.36em;
  border-radius:50%;
  background:currentColor;
  opacity:.32;
}


/* ── 5. Static state — also the prefers-reduced-motion state ───────────── */

/* Nothing to write. The rules above already render the completed thread:
   .rc-scroll has no transform (translateY(0) = fully scrolled), every bubble
   and chip is at its natural opacity 1, and the three typing pills are the
   only thing switched off. A reduced-motion reader sees all three turns,
   both source chips and the "can't tell" answer, with zero movement. */


/* ── 6. Timeline ───────────────────────────────────────────────────────── */

@media (prefers-reduced-motion:no-preference){

  /* The thread itself: one transform track for the scroll, one opacity track
     for the outro. Separate animations so each gets its own easing. */
  #anim-recall .rc-scroll{
    animation:
      rcScroll var(--rc-loop) var(--rc-ease) infinite,
      rcFade   var(--rc-loop) linear          infinite;
  }

  /* Each value is N = (b - 1.1) / 2 for the element that has just landed, so
     the revealed block stays centred. Every pair (a%,b%) is a hold; the gap
     between pairs is the move. Monotonically decreasing — the thread only
     ever travels upward. */
  @keyframes rcScroll{
    0%,     6% { transform:translateY(13.35em) }  /* Q1        b 27.8       */
    7.5%,  13% { transform:translateY(11.8em)  }  /* typing 1  b 24.7       */
    14.5%, 16% { transform:translateY(11.1em)  }  /* answer 1  b 23.3       */
    17.5%, 31% { transform:translateY(10em)    }  /* chip 1    b 21.1       */
    32.5%, 36% { transform:translateY(7.55em)  }  /* Q2        b 16.2       */
    37.5%, 43% { transform:translateY(6em)     }  /* typing 2  b 13.1       */
    44.5%, 46% { transform:translateY(5.3em)   }  /* answer 2  b 11.7       */
    47.5%, 61% { transform:translateY(4.2em)   }  /* chip 2    b  9.5       */
    62.5%, 66% { transform:translateY(2.45em)  }  /* Q3        b  6.0       */
    67.5%, 74% { transform:translateY(.9em)    }  /* typing 3  b  2.9       */
    75.5%, 93% { transform:translateY(-.5em)   }  /* answer 3 — thread full */
    97.5%,100% { transform:translateY(-1em)    }  /* drift up while fading  */
  }

  /* The third answer holds for 3.8s — it is the line that has to land — then
     the thread fades over 0.9s. The reset beat is only 0.5s of empty frame
     before the next question starts arriving. */
  @keyframes rcFade{
    0%          { opacity:0 }
    .4%,   93%  { opacity:1 }
    97.5%,100%  { opacity:0 }
  }

  /* Questions: up from the bottom-right, settling. ~0.55s each. */
  #anim-recall .rc-q1{ animation:rcQ1 var(--rc-loop) var(--rc-ease) infinite }
  #anim-recall .rc-q2{ animation:rcQ2 var(--rc-loop) var(--rc-ease) infinite }
  #anim-recall .rc-q3{ animation:rcQ3 var(--rc-loop) var(--rc-ease) infinite }

  @keyframes rcQ1{
    0%,     .4% { opacity:0; transform:translate(.5em,1.4em) scale(.97) }
    3.15%, 100% { opacity:1; transform:none }
  }
  @keyframes rcQ2{
    0%,     31% { opacity:0; transform:translate(.5em,1.4em) scale(.97) }
    33.75%,100% { opacity:1; transform:none }
  }
  @keyframes rcQ3{
    0%,     61% { opacity:0; transform:translate(.5em,1.4em) scale(.97) }
    63.75%,100% { opacity:1; transform:none }
  }

  /* Typing pills: in over 0.3s, out over 0.14s as the answer takes its place. */
  #anim-recall .rc-t1{ animation:rcT1 var(--rc-loop) var(--rc-ease) infinite }
  #anim-recall .rc-t2{ animation:rcT2 var(--rc-loop) var(--rc-ease) infinite }
  #anim-recall .rc-t3{ animation:rcT3 var(--rc-loop) var(--rc-ease) infinite }

  @keyframes rcT1{
    0%,      6% { opacity:0; transform:translateY(.5em) }
    7.5%,  13% { opacity:1; transform:none }
    13.7%,100% { opacity:0; transform:translateY(-.25em) }
  }
  @keyframes rcT2{
    0%,     36% { opacity:0; transform:translateY(.5em) }
    37.5%, 43% { opacity:1; transform:none }
    43.7%,100% { opacity:0; transform:translateY(-.25em) }
  }
  @keyframes rcT3{
    0%,     66% { opacity:0; transform:translateY(.5em) }
    67.5%, 74% { opacity:1; transform:none }
    74.7%,100% { opacity:0; transform:translateY(-.25em) }
  }

  /* Calm three-dot pulse. The only always-on animation in the file; the pills
     it lives in are opacity:0 for most of the loop. */
  #anim-recall .rc-typing i{ animation:rcDot 1.5s var(--ease,cubic-bezier(.28,.11,.32,1)) infinite }
  #anim-recall .rc-typing i:nth-child(2){ animation-delay:.16s }
  #anim-recall .rc-typing i:nth-child(3){ animation-delay:.32s }

  @keyframes rcDot{
    0%,70%,100% { opacity:.28; transform:translateY(0) }
    32%         { opacity:.9;  transform:translateY(-.16em) }
  }

  /* Answers: a quiet rise, no lateral movement — it should feel considered. */
  #anim-recall .rc-a1{ animation:rcA1 var(--rc-loop) var(--rc-ease) infinite }
  #anim-recall .rc-a2{ animation:rcA2 var(--rc-loop) var(--rc-ease) infinite }
  #anim-recall .rc-a3{ animation:rcA3 var(--rc-loop) var(--rc-ease) infinite }

  @keyframes rcA1{
    0%,     13% { opacity:0; transform:translateY(.7em) }
    15.75%,100% { opacity:1; transform:none }
  }
  @keyframes rcA2{
    0%,     43% { opacity:0; transform:translateY(.7em) }
    45.75%,100% { opacity:1; transform:none }
  }
  @keyframes rcA3{
    0%,     74% { opacity:0; transform:translateY(.7em) }
    76.75%,100% { opacity:1; transform:none }
  }

  /* Chips land 0.6s behind their answer — a citation being attached, not
     part of the same gesture. */
  #anim-recall .rc-c1{ animation:rcC1 var(--rc-loop) var(--rc-ease) infinite }
  #anim-recall .rc-c2{ animation:rcC2 var(--rc-loop) var(--rc-ease) infinite }

  @keyframes rcC1{
    0%,     16% { opacity:0; transform:translateY(.45em) }
    18.25%,100% { opacity:1; transform:none }
  }
  @keyframes rcC2{
    0%,     46% { opacity:0; transform:translateY(.45em) }
    48.25%,100% { opacity:1; transform:none }
  }
}
