/* ============================================================================
   Native Apps Team — anim-name.css
   Two looping process diagrams: #anim-autoname and #anim-autosort.

   Owns ONLY the two <figure class="anim"> blocks in assets/anim-name.svg.html.
   Additive: every colour, radius and easing comes from assets/tokens.css.
   No token name is redefined at :root. No JS. No external requests.

   HOW THE TIMING WORKS
     Every animated element shares one master period per figure
     (--an-loop: 7s, --as-loop: 16s) and expresses its beats as percentages
     of that period. Nothing uses animation-delay for sequencing, so the two
     figures can never drift out of step with themselves.

   REDUCED MOTION
     The rules OUTSIDE @media (prefers-reduced-motion: no-preference) draw the
     finished state: AutoName shows the resolved filename with a check, and
     AutoSort shows the document already sitting in the lifted Legal folder
     with its count at 4. The animation block only ever ADDS `animation:`
     declarations (plus visibility for the second AutoSort run), so removing
     motion can never leave an empty box.

   Sections
     1  Shared shell            4  A · timeline
     2  Shared paint            5  B · AutoSort static state
     3  A · AutoName static     6  B · timeline
   ========================================================================= */


/* ── 1. Shared shell ───────────────────────────────────────────────────── */

.anim{
  /* Capped so the SVG type never balloons on a 1440px viewport. Both figures
     use a 360x288 viewBox, so the drawing only ever scales between 0.71x
     (320px viewport, inside .wrap) and 1.33x. That keeps the folder names —
     19 user units, the smallest type that must always read — between 13.4px
     and 25px. Anything smaller than 19 units in here is transient. */
  max-width:520px;
  margin-inline:auto;
  padding:clamp(.75rem,2.5vw,1.25rem);
  background:var(--paper-2);
  border:1px solid var(--hair);
  border-radius:var(--radius);
}

.anim svg{
  display:block;
  width:100%;
  height:auto;                       /* viewBox drives the aspect ratio */
  font-family:inherit;               /* pick up the body stack, not Times */
}

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


/* ── 2. Shared paint ───────────────────────────────────────────────────── */

/* Paper stock for anything that reads as a document. */
.anim .sheet{ fill:var(--card); stroke:var(--ink); stroke-opacity:.16; stroke-width:1 }

/* Abstract body copy. Two stacked passes: a permanent dim rail and a bright
   overlay whose OPACITY animates, so nothing ever animates `fill`. */
.anim .rail{ fill:var(--ink); opacity:.14 }
.anim .lit { fill:var(--ink); opacity:.72 }
.anim .lit--title{ opacity:1 }

.anim text{ fill:var(--ink) }
.anim .mono{
  font-family:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
  font-size:17.5px;                  /* px inside an SVG == user units */
  letter-spacing:0;
}

/* transform-box:fill-box makes every transform-origin below resolve against
   the element's own bounding box instead of the viewBox origin, so scale and
   translate can be written without hand-computing user-space coordinates. */
.anim .fx{ transform-box:fill-box; transform-origin:center }


/* ════════════════════════════════════════════════════════════════════════
   3. A · AutoName — static (also the prefers-reduced-motion state)
   ═══════════════════════════════════════════════════════════════════════ */

#anim-autoname{ --an-loop:7s }

/* Scan band and the pre-rename filename only exist mid-animation. */
#anim-autoname .an-sweep,
#anim-autoname .an-name-old,
#anim-autoname .an-shuffle,
#anim-autoname .an-caret{ opacity:0 }

#anim-autoname .an-sweep-edge{ stop-color:var(--cobalt); stop-opacity:0 }
#anim-autoname .an-sweep-core{ stop-color:var(--cobalt); stop-opacity:.5 }

/* Marker beside the line the model picked as the title. Redundant with the
   line's greater thickness, so the meaning never rests on colour alone. */
#anim-autoname .an-mark{ fill:var(--cobalt) }

#anim-autoname .an-field{ fill:var(--card); stroke:var(--hair); stroke-width:1 }

/* The typewriter is a clip that grows in 25 discrete steps — one per glyph.
   Resolved (full width) by default, so reduced motion shows the whole name. */
#anim-autoname .an-wipe{
  transform-box:fill-box; transform-origin:left center;
  transform:scaleX(1);
}

#anim-autoname .an-name-old,
#anim-autoname .an-shuffle{ fill:var(--ink-2) }
#anim-autoname .an-name-new{ fill:var(--ink); font-weight:500 }
#anim-autoname .an-caret{ fill:var(--cobalt) }

/* Success disc. --cobalt is fixed across both schemes, so a white glyph on it
   holds ~5.4:1 in light and dark alike; --accent would not. */
#anim-autoname .an-check-disc{ fill:var(--cobalt) }
#anim-autoname .an-check-tick{
  fill:none; stroke:#fff; stroke-width:2.4;
  stroke-linecap:round; stroke-linejoin:round;
}


/* ════════════════════════════════════════════════════════════════════════
   4. A · AutoName — timeline (7s)
   ═══════════════════════════════════════════════════════════════════════ */

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

  #anim-autoname .an-stage  { animation:anStage  var(--an-loop) linear infinite }
  #anim-autoname .an-doc    { animation:anDocIn  var(--an-loop) var(--ease-out) infinite }
  #anim-autoname .an-sweep  { animation:anSweep  var(--an-loop) linear infinite }

  #anim-autoname .an-lit-1  { animation:anLit1 var(--an-loop) linear infinite }
  #anim-autoname .an-lit-2  { animation:anLit2 var(--an-loop) linear infinite }
  #anim-autoname .an-lit-3  { animation:anLit3 var(--an-loop) linear infinite }
  #anim-autoname .an-lit-4  { animation:anLit4 var(--an-loop) linear infinite }
  #anim-autoname .an-lit-5  { animation:anLit5 var(--an-loop) linear infinite }
  #anim-autoname .an-mark   { animation:anMark var(--an-loop) var(--ease-out) infinite }

  #anim-autoname .an-name-old{ animation:anOld  var(--an-loop) linear infinite }
  #anim-autoname .an-shuffle-1{ animation:anShuf1 var(--an-loop) steps(1,jump-start) infinite }
  #anim-autoname .an-shuffle-2{ animation:anShuf2 var(--an-loop) steps(1,jump-start) infinite }
  #anim-autoname .an-shuffle-3{ animation:anShuf3 var(--an-loop) steps(1,jump-start) infinite }

  /* 25 glyphs, 25 steps: the clip edge lands exactly on a character boundary
     every time, which is what sells it as typing rather than a wipe. */
  #anim-autoname .an-wipe { animation:anWipe var(--an-loop) steps(25,jump-start) infinite }
  /* Two animations, two properties: the caret hops in the same 25 steps as
     the clip, but fades on a plain linear curve. */
  #anim-autoname .an-caret{
    animation:anCaret var(--an-loop) steps(25,jump-start) infinite,
              anCaretO var(--an-loop) linear infinite;
  }
  #anim-autoname .an-check{ animation:anCheck var(--an-loop) var(--ease-out) infinite }

  /* Global fade in / out. Everything else only carries its own local beat. */
  @keyframes anStage{
    0%,2.86%{ opacity:0 }
    12.86%,85.71%{ opacity:1 }
    94.29%,100%{ opacity:0 }
  }
  /* 0.0–0.9s · the page arrives */
  @keyframes anDocIn{
    0%,2.86%{ transform:translateY(16px) }
    12.86%,100%{ transform:translateY(0) }
  }
  /* 0.9–2.6s · the read pass travels the full height of the page */
  @keyframes anSweep{
    0%,12.00%{ transform:translateY(-52px); opacity:0 }
    15.50%{ opacity:1 }
    33.00%{ opacity:1 }
    37.14%{ transform:translateY(168px); opacity:0 }
    100%{ transform:translateY(168px); opacity:0 }
  }
  /* Each rail brightens as the band crosses it. The % values are the band's
     actual arrival times, derived from its travel, not eyeballed. */
  @keyframes anLit1{ 0%,18.71%{opacity:0} 21.7%,85.71%{opacity:1} 94.29%,100%{opacity:0} }
  @keyframes anLit2{ 0%,21.80%{opacity:0} 24.8%,85.71%{opacity:.72} 94.29%,100%{opacity:0} }
  @keyframes anLit3{ 0%,24.01%{opacity:0} 27.0%,85.71%{opacity:.72} 94.29%,100%{opacity:0} }
  @keyframes anLit4{ 0%,26.21%{opacity:0} 29.2%,85.71%{opacity:.72} 94.29%,100%{opacity:0} }
  @keyframes anLit5{ 0%,28.42%{opacity:0} 31.4%,85.71%{opacity:.72} 94.29%,100%{opacity:0} }
  /* The title candidate is singled out a beat after it lights. */
  @keyframes anMark{
    0%,20%{ opacity:0; transform:scaleY(.2) }
    23%,100%{ opacity:1; transform:scaleY(1) }
  }
  /* 0.9–2.6s the machine name holds, then hands over */
  @keyframes anOld{
    0%,12.00%{ opacity:0 }
    14.00%,37.14%{ opacity:1 }
    39.29%,100%{ opacity:0 }
  }
  /* 2.75–3.2s · three hard cuts. steps(1) keeps them cuts, not crossfades. */
  @keyframes anShuf1{ 0%,39.28%{opacity:0} 39.29%,41.39%{opacity:1} 41.40%,100%{opacity:0} }
  @keyframes anShuf2{ 0%,41.39%{opacity:0} 41.40%,43.49%{opacity:1} 43.50%,100%{opacity:0} }
  @keyframes anShuf3{ 0%,43.49%{opacity:0} 43.50%,45.70%{opacity:1} 45.71%,100%{opacity:0} }
  /* 3.2–4.3s · the real name types itself in */
  @keyframes anWipe{
    0%,45.71%{ transform:scaleX(0) }
    61.43%,100%{ transform:scaleX(1) }
  }
  @keyframes anCaret{
    0%,45.71%{ transform:translateX(0) }
    61.43%,100%{ transform:translateX(285px) }
  }
  @keyframes anCaretO{
    0%,45.60%{ opacity:0 }
    45.71%,61.43%{ opacity:1 }
    63.50%,100%{ opacity:0 }
  }
  /* 4.45s · confirmation, then a 1.2s hold before the loop resets */
  @keyframes anCheck{
    0%,63.57%{ opacity:0; transform:scale(.6) }
    68.57%,100%{ opacity:1; transform:scale(1) }
  }
}


/* ════════════════════════════════════════════════════════════════════════
   5. B · AutoSort — static (also the prefers-reduced-motion state)
   Shows one settled outcome: run A's document filed under Legal, count at 4.
   ═══════════════════════════════════════════════════════════════════════ */

#anim-autosort{ --as-loop:16s }

#anim-autosort .as-back{ fill:var(--ink); opacity:.10 }
#anim-autosort .as-front{ fill:var(--card); stroke:var(--hair); stroke-width:1 }
#anim-autosort .as-tint{ fill:var(--cobalt); opacity:0 }
#anim-autosort .as-edge{ fill:none; stroke:var(--cobalt); stroke-width:1.5; opacity:0 }

/* Folder names never dim: at the 320px floor this is ~12.8px, which is the
   smallest type in either figure and the only text that must always read. */
#anim-autosort .as-label{ font-size:19px; font-weight:500; fill:var(--ink) }
#anim-autosort .as-count{ font-size:22px; font-weight:600; fill:var(--ink) }
#anim-autosort .as-count--idle{ fill:var(--ink-2) }

#anim-autosort .as-chip{ fill:var(--card); stroke:var(--hair); stroke-width:1 }
#anim-autosort .as-chip-on{ fill:var(--cobalt); opacity:.10 }
#anim-autosort .as-chip-edge{ fill:none; stroke:var(--cobalt); stroke-width:1.5 }
#anim-autosort .as-guess{
  font-family:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
  font-size:15.5px; fill:var(--ink-2);
}
#anim-autosort .as-settled{ font-size:16px; font-weight:600; fill:var(--ink) }
#anim-autosort .as-tick{
  fill:none; stroke:var(--cobalt); stroke-width:2.2;
  stroke-linecap:round; stroke-linejoin:round;
}

/* Rejected guesses never show at rest. */
#anim-autosort .as-guess,
#anim-autosort .as-chip{ opacity:0 }

/* Settled state of run A. */
#anim-autosort .as-a .as-chip,
#anim-autosort .as-a .as-decided{ opacity:1 }

/* The lifted, receiving folder. */
#anim-autosort .as-folder--legal{ transform:translateY(-7px) }
#anim-autosort .as-folder--legal .as-tint{ opacity:.12 }
#anim-autosort .as-folder--legal .as-edge{ opacity:.5 }

/* Counts: Legal has already ticked, Receipts has not. */
#anim-autosort .as-legal-3{ opacity:0 }
#anim-autosort .as-legal-4{ opacity:1 }
#anim-autosort .as-recp-7{ opacity:1 }
#anim-autosort .as-recp-8{ opacity:0 }

/* Run A's document, parked in the folder. Run B has not happened yet, so it
   is hidden outright — visibility, not opacity, so it also leaves the a11y
   tree and cannot be tabbed or read out of order. */
#anim-autosort .as-a-x{ transform:translateX(0) }
#anim-autosort .as-a-y{ transform:translateY(127px) }
#anim-autosort .as-a-s{ transform:scale(.6) }
#anim-autosort .as-b{ visibility:hidden }

/* With no motion the chip has no document beside it to belong to, so it is
   parked over the folder it chose. Reset inside the animation block. */
#anim-autosort .as-a .as-chip{ transform:translate(-51px,56px) }
#anim-autosort .as-a .as-decided{ transform:translate(-51px,56px) }


/* ════════════════════════════════════════════════════════════════════════
   6. B · AutoSort — timeline (16s = two 8s runs, different doc, different
   folder). One period covers both runs, so the loop never reads as a GIF.
   ═══════════════════════════════════════════════════════════════════════ */

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

  #anim-autosort .as-b{ visibility:visible }
  /* In motion the chip rides beside its document, not above the folder. */
  #anim-autosort .as-a .as-chip{ transform:none }

  /* — run A: 0.5s–8.8s, routes to Legal — */
  #anim-autosort .as-a    { animation:asStageA var(--as-loop) linear infinite }
  #anim-autosort .as-a-x  { animation:asAX  var(--as-loop) var(--ease-out) infinite }
  #anim-autosort .as-a-y  { animation:asAY  var(--as-loop) cubic-bezier(.65,0,.35,1) infinite }
  /* Scale rides the same curve as Y, so the page shrinks as it falls away
     rather than deflating in mid-air before it has gone anywhere. */
  #anim-autosort .as-a-s  { animation:asAS  var(--as-loop) cubic-bezier(.65,0,.35,1) infinite }
  #anim-autosort .as-a .as-chip     { animation:asChipA var(--as-loop) var(--ease-out) infinite }
  #anim-autosort .as-a .as-guess-1  { animation:asGA1 var(--as-loop) steps(1,jump-start) infinite }
  #anim-autosort .as-a .as-guess-2  { animation:asGA2 var(--as-loop) steps(1,jump-start) infinite }
  #anim-autosort .as-a .as-decided  { animation:asDecA var(--as-loop) var(--ease-out) infinite }

  /* — run B: 8.2s–15.8s, routes to Receipts — */
  #anim-autosort .as-b    { animation:asStageB var(--as-loop) linear infinite }
  #anim-autosort .as-b-x  { animation:asBX  var(--as-loop) var(--ease-out) infinite }
  #anim-autosort .as-b-y  { animation:asBY  var(--as-loop) cubic-bezier(.65,0,.35,1) infinite }
  #anim-autosort .as-b-s  { animation:asBS  var(--as-loop) cubic-bezier(.65,0,.35,1) infinite }
  #anim-autosort .as-b .as-chip     { animation:asChipB var(--as-loop) var(--ease-out) infinite }
  #anim-autosort .as-b .as-guess-1  { animation:asGB1 var(--as-loop) steps(1,jump-start) infinite }
  #anim-autosort .as-b .as-guess-2  { animation:asGB2 var(--as-loop) steps(1,jump-start) infinite }
  #anim-autosort .as-b .as-decided  { animation:asDecB var(--as-loop) var(--ease-out) infinite }

  /* — folders — */
  #anim-autosort .as-folder--legal{ animation:asLiftL var(--as-loop) var(--ease-out) infinite }
  #anim-autosort .as-folder--legal .as-tint{ animation:asGlowL var(--as-loop) linear infinite }
  #anim-autosort .as-folder--legal .as-edge{ animation:asEdgeL var(--as-loop) linear infinite }
  #anim-autosort .as-folder--recp { animation:asLiftR var(--as-loop) var(--ease-out) infinite }
  #anim-autosort .as-folder--recp .as-tint{ animation:asGlowR var(--as-loop) linear infinite }
  #anim-autosort .as-folder--recp .as-edge{ animation:asEdgeR var(--as-loop) linear infinite }

  #anim-autosort .as-legal-3{ animation:asOldL var(--as-loop) var(--ease-out) infinite }
  #anim-autosort .as-legal-4{ animation:asNewL var(--as-loop) var(--ease-out) infinite }
  #anim-autosort .as-recp-7 { animation:asOldR var(--as-loop) var(--ease-out) infinite }
  #anim-autosort .as-recp-8 { animation:asNewR var(--as-loop) var(--ease-out) infinite }

  /* ---- run A keyframes ---- */
  @keyframes asStageA{
    0%,3.13%{ opacity:0 }
    8.75%,51.25%{ opacity:1 }
    55%,100%{ opacity:0 }
  }
  /* X finishes early (--ease-out), Y is still accelerating: the two together
     draw the arc. Two axes beat one offset-path for browser support. */
  @keyframes asAX{ 0%,23.75%{ transform:translateX(-48px) } 31.25%,100%{ transform:translateX(0) } }
  @keyframes asAY{
    0%,3.13%{ transform:translateY(-30px) }
    8.75%,23.75%{ transform:translateY(-14px) }
    31.25%,100%{ transform:translateY(127px) }
  }
  @keyframes asAS{ 0%,23.75%{ transform:scale(1) } 31.25%,100%{ transform:scale(.6) } }

  @keyframes asChipA{
    0%,8.13%{ opacity:0 }
    8.75%,29%{ opacity:1 }
    32%,100%{ opacity:0 }
  }
  @keyframes asGA1{ 0%,8.74%{opacity:0} 8.75%,13.74%{opacity:1} 13.75%,100%{opacity:0} }
  @keyframes asGA2{ 0%,13.74%{opacity:0} 13.75%,18.12%{opacity:1} 18.13%,100%{opacity:0} }
  @keyframes asDecA{
    0%,18.13%{ opacity:0; transform:scale(.92) }
    20.5%,29%{ opacity:1; transform:scale(1) }
    32%,100%{ opacity:0; transform:scale(1) }
  }

  /* ---- run B keyframes (run A shifted +50%, retargeted to Receipts) ---- */
  @keyframes asStageB{
    0%,51.25%{ opacity:0 }
    56.25%,95%{ opacity:1 }
    98.75%,100%{ opacity:0 }
  }
  @keyframes asBX{ 0%,71.25%{ transform:translateX(48px) } 78.75%,100%{ transform:translateX(-112px) } }
  @keyframes asBY{
    0%,51.25%{ transform:translateY(-30px) }
    56.25%,71.25%{ transform:translateY(-14px) }
    78.75%,100%{ transform:translateY(127px) }
  }
  @keyframes asBS{ 0%,71.25%{ transform:scale(1) } 78.75%,100%{ transform:scale(.6) } }

  @keyframes asChipB{
    0%,55.63%{ opacity:0 }
    56.25%,76.5%{ opacity:1 }
    79.5%,100%{ opacity:0 }
  }
  @keyframes asGB1{ 0%,56.24%{opacity:0} 56.25%,61.24%{opacity:1} 61.25%,100%{opacity:0} }
  @keyframes asGB2{ 0%,61.24%{opacity:0} 61.25%,65.62%{opacity:1} 65.63%,100%{opacity:0} }
  @keyframes asDecB{
    0%,65.63%{ opacity:0; transform:scale(.92) }
    68%,76.5%{ opacity:1; transform:scale(1) }
    79.5%,100%{ opacity:0; transform:scale(1) }
  }

  /* ---- folders ---- */
  /* Legal receives at 5.0s and stands down at 8.5s, while attention has
     already moved to the next document entering at the top. */
  @keyframes asLiftL{
    0%,31.25%{ transform:translateY(0) }
    33.75%,53.13%{ transform:translateY(-7px) }
    56.25%,100%{ transform:translateY(0) }
  }
  @keyframes asGlowL{
    0%,31.25%{ opacity:0 }
    33.75%,53.13%{ opacity:.12 }
    56.25%,100%{ opacity:0 }
  }
  @keyframes asEdgeL{
    0%,31.25%{ opacity:0 }
    33.75%,53.13%{ opacity:.5 }
    56.25%,100%{ opacity:0 }
  }
  /* Receipts receives at 13.0s; its stand-down wraps past 0% into the next
     loop, which is why its keyframes start in the raised state. */
  @keyframes asLiftR{
    0%,3.75%{ transform:translateY(-7px) }
    7.5%,78.75%{ transform:translateY(0) }
    81.25%,100%{ transform:translateY(-7px) }
  }
  @keyframes asGlowR{
    0%,3.75%{ opacity:.12 }
    7.5%,78.75%{ opacity:0 }
    81.25%,100%{ opacity:.12 }
  }
  @keyframes asEdgeR{
    0%,3.75%{ opacity:.5 }
    7.5%,78.75%{ opacity:0 }
    81.25%,100%{ opacity:.5 }
  }

  /* Counts: the outgoing figure leaves upward, the incoming arrives from
     below; the reset back to the old figure is a plain crossfade in place. */
  /* The tick itself is the beat: the old figure leaves upward, the new one
     arrives from below. The reset back to the old figure is not a beat, so it
     is a single-frame swap (two stops 0.01% apart) hidden under the moment the
     folder finishes standing down — a crossfade there either ghosts both
     digits or, worse, leaves the pocket briefly empty. */
  @keyframes asOldL{
    0%,32.5%{ opacity:1; transform:translateY(0) }
    34.38%{ opacity:0; transform:translateY(-9px) }
    56.19%{ opacity:0; transform:translateY(0) }
    56.20%,100%{ opacity:1; transform:translateY(0) }
  }
  @keyframes asNewL{
    0%,32.5%{ opacity:0; transform:translateY(9px) }
    34.38%,56.19%{ opacity:1; transform:translateY(0) }
    56.20%,100%{ opacity:0; transform:translateY(0) }
  }
  @keyframes asOldR{
    0%,7.44%{ opacity:0; transform:translateY(0) }
    7.45%,80%{ opacity:1; transform:translateY(0) }
    82%,100%{ opacity:0; transform:translateY(-9px) }
  }
  @keyframes asNewR{
    0%,7.44%{ opacity:1; transform:translateY(0) }
    7.45%{ opacity:0; transform:translateY(0) }
    80%{ opacity:0; transform:translateY(9px) }
    82%,100%{ opacity:1; transform:translateY(0) }
  }
}
