/* ════════════════════════════════════════════════════════════════════════
   AssetFlowPro lockup — the one stylesheet for the mark.

   Pairs with templates/_afp_mark.html. Loaded by base.html (the whole signed-in
   app), login.html and landing.html. It used to be an inline partial
   (_afp_logo_style.html) that only the two standalone pages could reach; the
   sidebar brand needs the same artwork, and two copies of 200 lines of gradient
   plumbing is how a logo drifts from itself.

   Vector rebuild of the AssetFlowPro lockup, replacing the 217KB PNG that
   carried a baked-in black plate. The plate could not be keyed out of the raster
   (the sphere and the letter counters are black too, and the source carries
   compression noise), so the mark was retraced. Transparent in both themes;
   assembles once on load and then holds still.

   NOTE the wordmark group is .afp-lettermark, NOT .afp-word — .afp-word is the
   rotating tagline word on the login page, and its translate(-50%,-50%) resolves
   against the viewBox on an SVG child, throwing the wordmark onto the sphere.

   Every gradient stop in the SVG carries a ramp CLASS, not a colour: the metal is
   declared once, here. a0-a12 gold (brightest first, sampled off the artwork),
   w0-w8 the wordmark's own gold, p0-p5 the silver of 'Pro', h/d the specular and
   shadow overlays (neutral on purpose: they carry the modelling, not the colour,
   so they survive the theme swap), s0-s2+gl the globe. The SVG and this file are
   one object.
   ════════════════════════════════════════════════════════════════════════ */

.afp-logo {
  --au0:#fff8d2; --au1:#fdf0a0; --au2:#fbe479; --au3:#f7d263; --au4:#eebe53;
  --au5:#dfa845; --au6:#c68f34; --au7:#ac7a2b; --au8:#926524; --au9:#79511c;
  --au10:#5e3f14; --au11:#432c0d; --au12:#2a1c07;
  --w0:#fdf0a0; --w2:#fbe479; --w3:#f7d263; --w4:#eebe53;
  --w5:#dfa845; --w6:#c68f34; --w7:#ac7a2b; --w8:#926524;
  --p0:#fafafa; --p1:#e6e8e6; --p2:#cfd0ce; --p3:#b2b3b3; --p4:#97989a; --p5:#7c7a80;
  --hi:#fff0bf; --sp0:#26324c; --sp1:#0e1727; --sp2:#05080f; --gloss:#dce7ff;
  --shade:1;

  display: block;
  width: var(--afp-logo-w, 260px);
  max-width: 76%;
  height: auto;
  margin: 0 auto 6px;
  overflow: visible;              /* the entrance leans outside the box */
}

[data-theme="light"] .afp-logo {
  --au0:#e0b95f; --au1:#d3a747; --au2:#c39638; --au3:#b3882e; --au4:#a67d2a;
  --au5:#977126; --au6:#856322; --au7:#73551d; --au8:#614819; --au9:#503b14;
  --au10:#3e2e10; --au11:#2d210b; --au12:#1c1406;
  --w0:#a87c25; --w2:#9d7322; --w3:#91691f; --w4:#85601c;
  --w5:#785619; --w6:#6a4b16; --w7:#5b4012; --w8:#4b350f;
  --p0:#838b96; --p1:#767d88; --p2:#656c76; --p3:#525963; --p4:#3f454e; --p5:#2a3038;
  --hi:#ffeeb4; --sp0:#f7f9fc; --sp1:#dde3ed; --sp2:#c3ccdb; --gloss:#fff;
  --shade:.55;
}

/* Light: each step keeps 41-57% of its dark relative luminance. Measured on
   #edf0f5: mark median 3.2:1 (94% at or above 2:1), wordmark median 4.1:1 (90%
   at or above 3:1) on its own darker ramp, 'Pro' graphite 3:1 to 11:1.
   Modelling drops to 55%: a half-alpha highlight over dark gold on a light page
   bleaches the band carrying the contrast. */

.afp-logo .a0{stop-color:var(--au0)} .afp-logo .a1{stop-color:var(--au1)}
.afp-logo .a2{stop-color:var(--au2)} .afp-logo .a3{stop-color:var(--au3)}
.afp-logo .a4{stop-color:var(--au4)} .afp-logo .a5{stop-color:var(--au5)}
.afp-logo .a6{stop-color:var(--au6)} .afp-logo .a7{stop-color:var(--au7)}
.afp-logo .a8{stop-color:var(--au8)} .afp-logo .a9{stop-color:var(--au9)}
.afp-logo .a10{stop-color:var(--au10)} .afp-logo .a11{stop-color:var(--au11)}
.afp-logo .a12{stop-color:var(--au12)}
.afp-logo .w0,.afp-logo .w1{stop-color:var(--w0)} .afp-logo .w2{stop-color:var(--w2)}
.afp-logo .w3{stop-color:var(--w3)} .afp-logo .w4{stop-color:var(--w4)}
.afp-logo .w5{stop-color:var(--w5)} .afp-logo .w6{stop-color:var(--w6)}
.afp-logo .w7{stop-color:var(--w7)} .afp-logo .w8{stop-color:var(--w8)}
.afp-logo .p0{stop-color:var(--p0)} .afp-logo .p1{stop-color:var(--p1)}
.afp-logo .p2{stop-color:var(--p2)} .afp-logo .p3{stop-color:var(--p3)}
.afp-logo .p4{stop-color:var(--p4)} .afp-logo .p5{stop-color:var(--p5)}
.afp-logo .h{stop-color:var(--hi)} .afp-logo .d{stop-color:#000}
.afp-logo .s0{stop-color:var(--sp0)} .afp-logo .s1{stop-color:var(--sp1)}
.afp-logo .s2{stop-color:var(--sp2)} .afp-logo .gl{stop-color:var(--gloss)}
.afp-logo .afp-shade{opacity:var(--shade)}

/* ENTRANCE. Resting state IS the finished state: every rule animates FROM a
   hidden keyframe INTO the element's own resting CSS, fill-mode backwards so the
   0% frame covers only the delay. Kill the animations and the logo is simply
   there, assembled. */
.afp-logo .afp-globe,
.afp-logo .afp-orb-back,
.afp-logo .afp-orb-front,
.afp-logo .afp-bar,
.afp-logo .afp-wipe,
.afp-logo .afp-sheen-band,
.afp-logo .afp-idle { transform-box: view-box; }

/* transform-origin lengths below are RAW user-space coordinates: Blink does not
   add the viewBox's min-x/min-y to them (probed, headless Edge 2026-09-23).
   That is what lets .afp-logo--mark crop the viewBox to 161 20 425 329 without
   re-basing a single origin. */

.afp-logo .afp-globe {
  transform-origin: 367px 177px;
  animation: afpGlobeIn 760ms cubic-bezier(.22,.78,.32,1) backwards;
}
@keyframes afpGlobeIn {
  from { opacity: 0; transform: scale(.9); }
  to   { opacity: 1; transform: none; }
}

.afp-logo .afp-orb-back {
  transform-origin: 367px 177px;
  animation: afpOrbBack 1040ms cubic-bezier(.16,.82,.26,1) 60ms backwards;
}
@keyframes afpOrbBack {
  from { opacity: 0; transform: rotate(-44deg) scale(.88); }
  to   { opacity: 1; transform: none; }
}
.afp-logo .afp-orb-front {
  transform-origin: 367px 177px;
  animation: afpOrbFront 1040ms cubic-bezier(.16,.82,.26,1) 190ms backwards;
}
@keyframes afpOrbFront {
  from { opacity: 0; transform: rotate(40deg) scale(.9); }
  to   { opacity: 1; transform: none; }
}

.afp-logo .afp-bar { animation: afpBar 640ms cubic-bezier(.2,.84,.3,1) backwards; }
.afp-logo .afp-bar:nth-child(1) { transform-origin: 311px 246px; animation-delay: 430ms; }
.afp-logo .afp-bar:nth-child(2) { transform-origin: 371px 229px; animation-delay: 520ms; }
.afp-logo .afp-bar:nth-child(3) { transform-origin: 431px 197px; animation-delay: 610ms; }
@keyframes afpBar {
  from { opacity: .2; transform: scaleY(.04); }
  to   { opacity: 1;  transform: none; }
}

/* The arrow is DRAWN, tail first, by a wipe whose edge runs perpendicular to the
   arrow's own axis (-29.5deg: tail 176,270 -> head 582,36). At rest the rect
   covers the whole canvas - which is why switching the animation off leaves the
   arrow whole instead of half-drawn. */
.afp-logo .afp-wipe {
  transform-origin: 373px 178px;
  transform: rotate(-29.5deg);
  animation: afpLaunch 1180ms cubic-bezier(.3,.66,.26,1) 700ms backwards;
}
@keyframes afpLaunch {
  from { transform: rotate(-29.5deg) translateX(-548px); }
  to   { transform: rotate(-29.5deg); }
}

.afp-logo .afp-ltr { animation: afpLetter 520ms cubic-bezier(.2,.8,.3,1) backwards; }
@keyframes afpLetter {
  from { opacity: 0; transform: translateY(9px); }
  to   { opacity: 1; transform: none; }
}
.afp-logo .afp-ltr:nth-child(1)  { animation-delay: 900ms; }
.afp-logo .afp-ltr:nth-child(2)  { animation-delay: 945ms; }
.afp-logo .afp-ltr:nth-child(3)  { animation-delay: 990ms; }
.afp-logo .afp-ltr:nth-child(4)  { animation-delay: 1035ms; }
.afp-logo .afp-ltr:nth-child(5)  { animation-delay: 1080ms; }
.afp-logo .afp-ltr:nth-child(6)  { animation-delay: 1125ms; }
.afp-logo .afp-ltr:nth-child(7)  { animation-delay: 1170ms; }
.afp-logo .afp-ltr:nth-child(8)  { animation-delay: 1215ms; }
.afp-logo .afp-ltr:nth-child(9)  { animation-delay: 1260ms; }
.afp-logo .afp-ltr:nth-child(10) { animation-delay: 1305ms; }
.afp-logo .afp-ltr:nth-child(11) { animation-delay: 1350ms; }
.afp-logo .afp-ltr:nth-child(12) { animation-delay: 1395ms; }

/* IDLE: about half a degree of tilt over 16s, one highlight crossing the metal
   every 12s, both starting only after the entrance settles. */
.afp-logo .afp-idle {
  transform-origin: 370px 185px;
  animation: afpBreathe 16s ease-in-out 2.1s infinite;
}
@keyframes afpBreathe {
  0%   { transform: rotate(-0.26deg) translateY(0.6px); }
  50%  { transform: rotate(0.26deg)  translateY(-0.9px); }
  100% { transform: rotate(-0.26deg) translateY(0.6px); }
}
.afp-logo .afp-sheen-band {
  transform-origin: 370px 185px;
  transform: rotate(-14deg) translateX(0);
  animation: afpSheen 12s cubic-bezier(.45,.05,.45,.95) 2.6s infinite;
}
@keyframes afpSheen {
  0%, 62%   { transform: rotate(-14deg) translateX(0); }
  86%, 100% { transform: rotate(-14deg) translateX(920px); }
}

/* ── Mark-only variant ────────────────────────────────────────────────────
   The lockup with afp_lettermark=false: the viewBox is cropped to the artwork
   (161 20 425 329 — the sampled curve bounds plus 3) so the mark can sit INLINE
   inside a typographic wordmark —
   ASSETFLOW [mark] PRO in the sidebar — with no empty band under it where the
   word used to be. Sized off --afp-mark-h (default 1.9 × the parent's font-size)
   with the width computed from the crop's aspect so no engine has to infer it.

   It is a separate size AND a separate tempo: at 24px the full 1.9s assembly is
   too slow to read, so the entrance is compressed to ~950ms below. */
.afp-logo--mark {
  display: inline-block;
  width: calc(var(--afp-mark-h, 1.9em) * 425 / 329);
  height: var(--afp-mark-h, 1.9em);
  max-width: none;
  margin: 0;
  vertical-align: middle;
  flex: 0 0 auto;
}

.afp-logo--mark .afp-globe     { animation-duration: 420ms; animation-delay:   0ms; }
.afp-logo--mark .afp-orb-back  { animation-duration: 520ms; animation-delay:  40ms; }
.afp-logo--mark .afp-orb-front { animation-duration: 520ms; animation-delay: 110ms; }
.afp-logo--mark .afp-bar       { animation-duration: 300ms; }
.afp-logo--mark .afp-bar:nth-child(1) { animation-delay: 190ms; }
.afp-logo--mark .afp-bar:nth-child(2) { animation-delay: 240ms; }
.afp-logo--mark .afp-bar:nth-child(3) { animation-delay: 290ms; }
.afp-logo--mark .afp-wipe      { animation-duration: 620ms; animation-delay: 330ms; }
.afp-logo--mark .afp-idle       { animation-delay: 1.1s; }
.afp-logo--mark .afp-sheen-band { animation-delay: 1.5s; }

/* SETTLED. The hero lockups on /login and / assemble on every visit — they are
   the point of those pages. The sidebar mark is on every single page of the app,
   and a mark that re-assembles on every navigation stops being a welcome and
   starts being a tic. base.html's pre-paint script stamps data-afp-settled once
   per TAB (sessionStorage), so the first page you open plays it and the next
   hundred simply have it, still breathing. Clear the tab, see it again. */
html[data-afp-settled="1"] .afp-logo--mark .afp-globe,
html[data-afp-settled="1"] .afp-logo--mark .afp-orb-back,
html[data-afp-settled="1"] .afp-logo--mark .afp-orb-front,
html[data-afp-settled="1"] .afp-logo--mark .afp-bar,
html[data-afp-settled="1"] .afp-logo--mark .afp-wipe { animation: none; }
html[data-afp-settled="1"] .afp-logo--mark .afp-idle       { animation-delay: 0s; }
html[data-afp-settled="1"] .afp-logo--mark .afp-sheen-band { animation-delay: 2s; }

/* Reduced motion: no entrance, no idle, no sheen, and nothing to restore. The
   sheen band parks outside the artwork, so switching it off removes it rather
   than freezing a bright bar across the mark. Verified: this render is
   pixel-identical to the settled one. */
@media (prefers-reduced-motion: reduce) {
  .afp-logo .afp-globe,
  .afp-logo .afp-orb-back,
  .afp-logo .afp-orb-front,
  .afp-logo .afp-bar,
  .afp-logo .afp-wipe,
  .afp-logo .afp-ltr,
  .afp-logo .afp-idle,
  .afp-logo .afp-sheen-band { animation: none !important; }
}
