eklavya 1.86.0 → 1.87.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/assets/dashboard.html +56 -8
  2. package/dist/assets/mascot/expressions/celebrate-body.svg +24 -8
  3. package/dist/assets/mascot/expressions/celebrate-face.svg +24 -8
  4. package/dist/assets/mascot/expressions/confused-body.svg +24 -8
  5. package/dist/assets/mascot/expressions/confused-face.svg +24 -8
  6. package/dist/assets/mascot/expressions/cool-body.svg +85 -0
  7. package/dist/assets/mascot/expressions/cool-face.svg +59 -0
  8. package/dist/assets/mascot/expressions/curious-body.svg +24 -8
  9. package/dist/assets/mascot/expressions/curious-face.svg +24 -8
  10. package/dist/assets/mascot/expressions/determined-body.svg +85 -0
  11. package/dist/assets/mascot/expressions/determined-face.svg +59 -0
  12. package/dist/assets/mascot/expressions/excited-body.svg +24 -8
  13. package/dist/assets/mascot/expressions/excited-face.svg +24 -8
  14. package/dist/assets/mascot/expressions/focused-body.svg +24 -8
  15. package/dist/assets/mascot/expressions/focused-face.svg +24 -8
  16. package/dist/assets/mascot/expressions/happy-body.svg +24 -8
  17. package/dist/assets/mascot/expressions/happy-face.svg +24 -8
  18. package/dist/assets/mascot/expressions/hopeful-body.svg +85 -0
  19. package/dist/assets/mascot/expressions/hopeful-face.svg +59 -0
  20. package/dist/assets/mascot/expressions/idea-body.svg +85 -0
  21. package/dist/assets/mascot/expressions/idea-face.svg +59 -0
  22. package/dist/assets/mascot/expressions/laugh-body.svg +85 -0
  23. package/dist/assets/mascot/expressions/laugh-face.svg +59 -0
  24. package/dist/assets/mascot/expressions/love-body.svg +85 -0
  25. package/dist/assets/mascot/expressions/love-face.svg +59 -0
  26. package/dist/assets/mascot/expressions/nervous-body.svg +85 -0
  27. package/dist/assets/mascot/expressions/nervous-face.svg +59 -0
  28. package/dist/assets/mascot/expressions/neutral-body.svg +24 -8
  29. package/dist/assets/mascot/expressions/neutral-face.svg +24 -8
  30. package/dist/assets/mascot/expressions/proud-body.svg +85 -0
  31. package/dist/assets/mascot/expressions/proud-face.svg +59 -0
  32. package/dist/assets/mascot/expressions/relieved-body.svg +85 -0
  33. package/dist/assets/mascot/expressions/relieved-face.svg +59 -0
  34. package/dist/assets/mascot/expressions/sad-body.svg +24 -8
  35. package/dist/assets/mascot/expressions/sad-face.svg +24 -8
  36. package/dist/assets/mascot/expressions/shy-body.svg +85 -0
  37. package/dist/assets/mascot/expressions/shy-face.svg +59 -0
  38. package/dist/assets/mascot/expressions/sleepy-body.svg +24 -8
  39. package/dist/assets/mascot/expressions/sleepy-face.svg +24 -8
  40. package/dist/assets/mascot/expressions/starstruck-body.svg +85 -0
  41. package/dist/assets/mascot/expressions/starstruck-face.svg +59 -0
  42. package/dist/assets/mascot/expressions/surprised-body.svg +24 -8
  43. package/dist/assets/mascot/expressions/surprised-face.svg +24 -8
  44. package/dist/assets/mascot/expressions/thinking-body.svg +24 -8
  45. package/dist/assets/mascot/expressions/thinking-face.svg +24 -8
  46. package/dist/assets/mascot/expressions/wink-body.svg +24 -8
  47. package/dist/assets/mascot/expressions/wink-face.svg +24 -8
  48. package/dist/assets/mascot/expressions/worried-body.svg +24 -8
  49. package/dist/assets/mascot/expressions/worried-face.svg +24 -8
  50. package/dist/assets/mascot/loading.svg +59 -36
  51. package/dist/assets/mascot/mascot.css +24 -8
  52. package/dist/assets/mascot/mascot.js +64 -7
  53. package/dist/assets/mascot.html +15 -11
  54. package/dist/plugin/.claude-plugin/plugin.json +1 -1
  55. package/package.json +1 -1
@@ -1295,7 +1295,7 @@
1295
1295
  <p class="page__eyebrow crumb inner" id="crumb" aria-label="Workflow and scope"></p>
1296
1296
  <div class="inner stack" id="view" aria-busy="true">
1297
1297
  <!-- The same markup `loader()` writes; the script has not run yet. -->
1298
- <div class="loader loader--page" role="status" aria-live="polite"><eklavya-mascot state="loading" variant="loading" size="96"></eklavya-mascot><span>Loading your history…</span></div>
1298
+ <div class="loader loader--page" role="status" aria-live="polite"><eklavya-mascot state="loading" variant="loading" size="96"></eklavya-mascot><span></span></div>
1299
1299
  </div>
1300
1300
  <span class="sr" id="fb-live" role="status" aria-live="polite"></span>
1301
1301
  <footer class="foot inner">
@@ -2304,13 +2304,61 @@ const chart = (id, fn) => {
2304
2304
  let GEN = 0;
2305
2305
 
2306
2306
  /**
2307
- * The one loading state: the companion drawing and releasing its arrow, and what is being waited
2308
- * for. `page: true` is the full-height version the first paint uses. Every
2309
- * placeholder a `slot()` starts with is one of these, so a wait looks
2310
- * the same wherever it happens. `label` is plain text; it is escaped here.
2307
+ * What a loader says while it waits: one of Claude Code's spinner verbs, instead of "Loading…".
2308
+ * "Clauding" is left out because it is Anthropic's product name. Present tense, one word,
2309
+ * nothing that sounds like an error.
2310
+ */
2311
+ const LOADER_VERBS = [
2312
+ 'Accomplishing', 'Actioning', 'Actualizing', 'Architecting', 'Baking', 'Beaming', "Beboppin'",
2313
+ 'Befuddling', 'Billowing', 'Blanching', 'Bloviating', 'Boogieing', 'Boondoggling', 'Booping',
2314
+ 'Bootstrapping', 'Brewing', 'Bunning', 'Burrowing', 'Calculating', 'Canoodling', 'Caramelizing',
2315
+ 'Cascading', 'Catapulting', 'Cerebrating', 'Channeling', 'Channelling', 'Choreographing',
2316
+ 'Churning', 'Coalescing', 'Cogitating', 'Combobulating', 'Composing', 'Computing', 'Concocting',
2317
+ 'Considering', 'Contemplating', 'Cooking', 'Crafting', 'Creating', 'Crunching', 'Crystallizing',
2318
+ 'Cultivating', 'Deciphering', 'Deliberating', 'Determining', 'Dilly-dallying',
2319
+ 'Discombobulating', 'Doing', 'Doodling', 'Drizzling', 'Ebbing', 'Effecting', 'Elucidating',
2320
+ 'Embellishing', 'Enchanting', 'Envisioning', 'Evaporating', 'Fermenting', 'Fiddle-faddling',
2321
+ 'Finagling', 'Flambéing', 'Flibbertigibbeting', 'Flowing', 'Flummoxing', 'Fluttering', 'Forging',
2322
+ 'Forming', 'Frolicking', 'Frosting', 'Gallivanting', 'Galloping', 'Garnishing', 'Generating',
2323
+ 'Gesticulating', 'Germinating', 'Gitifying', 'Grooving', 'Gusting', 'Harmonizing', 'Hashing',
2324
+ 'Hatching', 'Herding', 'Honking', 'Hullaballooing', 'Hyperspacing', 'Ideating', 'Imagining',
2325
+ 'Improvising', 'Incubating', 'Inferring', 'Infusing', 'Ionizing', 'Jitterbugging', 'Julienning',
2326
+ 'Kneading', 'Leavening', 'Levitating', 'Lollygagging', 'Manifesting', 'Marinating', 'Meandering',
2327
+ 'Metamorphosing', 'Misting', 'Moonwalking', 'Moseying', 'Mulling', 'Mustering', 'Musing',
2328
+ 'Nebulizing', 'Nesting', 'Newspapering', 'Noodling', 'Nucleating', 'Orbiting', 'Orchestrating',
2329
+ 'Osmosing', 'Perambulating', 'Percolating', 'Perusing', 'Philosophising', 'Photosynthesizing',
2330
+ 'Pollinating', 'Pondering', 'Pontificating', 'Pouncing', 'Precipitating', 'Prestidigitating',
2331
+ 'Processing', 'Proofing', 'Propagating', 'Puttering', 'Puzzling', 'Quantumizing',
2332
+ 'Razzle-dazzling', 'Razzmatazzing', 'Recombobulating', 'Reticulating', 'Roosting', 'Ruminating',
2333
+ 'Sautéing', 'Scampering', 'Schlepping', 'Scurrying', 'Seasoning', 'Shenaniganing', 'Shimmying',
2334
+ 'Simmering', 'Skedaddling', 'Sketching', 'Slithering', 'Smooshing', 'Sock-hopping', 'Spelunking',
2335
+ 'Spinning', 'Sprouting', 'Stewing', 'Sublimating', 'Swirling', 'Swooping', 'Symbioting',
2336
+ 'Synthesizing', 'Tempering', 'Thinking', 'Thundering', 'Tinkering', 'Tomfoolering',
2337
+ 'Topsy-turvying', 'Transfiguring', 'Transmuting', 'Twisting', 'Undulating', 'Unfurling',
2338
+ 'Unravelling', 'Vibing', 'Waddling', 'Wandering', 'Warping', 'Whatchamacalliting',
2339
+ 'Whirlpooling', 'Whirring', 'Whisking', 'Wibbling', 'Working', 'Wrangling', 'Zesting',
2340
+ 'Zigzagging',
2341
+ ];
2342
+ const randomVerb = () => LOADER_VERBS[Math.floor(Math.random() * LOADER_VERBS.length)] + '…';
2343
+ // Random for every wait, never fixed to a screen: the choices are dropped on each navigation.
2344
+ // Within one wait the verb holds, because a redraw writes the same markup and must not replace
2345
+ // a loader that is still waiting, and words changing under the reader read as progress steps.
2346
+ const loaderVerbs = new Map();
2347
+ function loaderVerb(label) {
2348
+ if (!loaderVerbs.has(label)) loaderVerbs.set(label, randomVerb());
2349
+ return loaderVerbs.get(label);
2350
+ }
2351
+ // The first paint's loader is in the markup, before this script; give it its verb now.
2352
+ document.querySelector('#view > .loader > span')?.replaceChildren(randomVerb());
2353
+
2354
+ /**
2355
+ * The one loading state: the companion walking toward the reader and a random verb, the only
2356
+ * thing it says, to screen readers too. `label` names the wait and is never shown: it keeps that
2357
+ * wait's verb through redraws. `page: true` is the full-height version the first paint uses. Every
2358
+ * placeholder a `slot()` starts with is one of these, so a wait looks the same wherever it happens.
2311
2359
  */
2312
2360
  function loader(label, { page = false } = {}) {
2313
- return `<div class="loader${page ? ' loader--page' : ''}" role="status" aria-live="polite"><eklavya-mascot state="loading" variant="loading" size="${page ? 96 : 56}"></eklavya-mascot><span>${esc(label)}</span></div>`;
2361
+ return `<div class="loader${page ? ' loader--page' : ''}" role="status" aria-live="polite"><eklavya-mascot state="loading" variant="loading" size="${page ? 96 : 56}"></eklavya-mascot><span>${loaderVerb(label)}</span></div>`;
2314
2362
  }
2315
2363
 
2316
2364
  /* ---------- the response cache ----------
@@ -2428,7 +2476,7 @@ const slotDone = (el) => { el.removeAttribute('aria-busy'); el.removeAttribute('
2428
2476
  const holdsContent = (el) => !el.hasAttribute('data-failed') && !el.querySelector(':scope > .slot__wait');
2429
2477
 
2430
2478
  /**
2431
- * `opts`: `label` (what the loader says), `cls` (a class for the target), `region` (the target is
2479
+ * `opts`: `label` (names the wait, so its loader keeps one verb), `cls` (a class for the target), `region` (the target is
2432
2480
  * a `data-region`, so a redraw replaces its contents alone). `draw` is a pure function of the
2433
2481
  * response, and must not change it: a cached body is drawn again. It may queue its own charts and slots.
2434
2482
  */
@@ -6781,7 +6829,7 @@ $('view').addEventListener('change', (e) => {
6781
6829
  }
6782
6830
  });
6783
6831
 
6784
- addEventListener('hashchange', () => { if (INV) { closePicker(false); syncFromUrl(); } });
6832
+ addEventListener('hashchange', () => { loaderVerbs.clear(); if (INV) { closePicker(false); syncFromUrl(); } });
6785
6833
 
6786
6834
  let resizeTimer = 0;
6787
6835
  addEventListener('resize', () => {
@@ -18,19 +18,35 @@ eklavya-mascot { display:inline-flex; flex-shrink:0; vertical-align:middle; line
18
18
  .ek-mascot { display:block; overflow:visible; flex-shrink:0; }
19
19
  .ek-mascot[data-variant="face"] { overflow:hidden; }
20
20
  .ek-mascot__face path:not([fill]) { fill:none; }
21
- .ek-mascot--loading .ek-mascot__character { animation:ek-breathe 2.8s ease-in-out infinite; transform-origin:95px 185px; }
22
- .ek-mascot--loading .ek-mascot__arrow { animation:ek-release 2.8s cubic-bezier(.3,0,.3,1) infinite; }
23
- .ek-mascot--loading .ek-mascot__string { animation:ek-draw 2.8s ease-in-out infinite; transform-origin:157px 125px; }
24
- .ek-mascot--loading .ek-mascot__ripple { animation:ek-hit 2.8s ease-out infinite; transform-origin:196px 128px; opacity:0; }
25
- @keyframes ek-breathe { 0%,100% {transform:translateY(0)} 35%,55% {transform:translateY(-3px) rotate(-1deg)} }
26
- @keyframes ek-release { 0%,12% {transform:translateX(0);opacity:1} 40%,52% {transform:translateX(-10px);opacity:1} 68% {transform:translateX(22px);opacity:0} 69%,85% {transform:translateX(0);opacity:0} 100% {transform:translateX(0);opacity:1} }
27
- @keyframes ek-draw { 0%,100% {transform:scaleX(1)} 40%,52% {transform:scaleX(1.11)} 60% {transform:scaleX(.08)} 68% {transform:scaleX(1)} }
28
- @keyframes ek-hit { 0%,60%,100% {opacity:0;transform:scale(.4)} 68% {opacity:.8;transform:scale(.7)} 88% {opacity:0;transform:scale(1.7)} }
29
21
  .ek-companion { display:flex;align-items:center;gap:12px;color:var(--dim);font:13px/1.5 var(--font-body); }
30
22
  .ek-companion--hero { margin-bottom:16px; }
31
23
  .ek-companion--hero b { display:block;color:var(--ink);font-weight:600; }
32
24
  .ek-mascot-message { display:flex;align-items:center;gap:16px;padding:16px 0; }
33
25
  .ek-mascot-message p {margin:0}
26
+ /* Secondary loader: draws and releases an arrow over 2.8 s. */
27
+ .ek-mascot--draw .ek-mascot__character { animation:ek-breathe 2.8s ease-in-out infinite; transform-origin:95px 185px; }
28
+ .ek-mascot--draw .ek-mascot__arrow { animation:ek-release 2.8s cubic-bezier(.3,0,.3,1) infinite; }
29
+ .ek-mascot--draw .ek-mascot__string { animation:ek-draw 2.8s ease-in-out infinite; transform-origin:157px 125px; }
30
+ .ek-mascot--draw .ek-mascot__ripple { animation:ek-hit 2.8s ease-out infinite; transform-origin:196px 128px; opacity:0; }
31
+ @keyframes ek-breathe { 0%,100% {transform:translateY(0)} 35%,55% {transform:translateY(-3px) rotate(-1deg)} }
32
+ @keyframes ek-release { 0%,12% {transform:translateX(0);opacity:1} 40%,52% {transform:translateX(-10px);opacity:1} 68% {transform:translateX(22px);opacity:0} 69%,85% {transform:translateX(0);opacity:0} 100% {transform:translateX(0);opacity:1} }
33
+ @keyframes ek-draw { 0%,100% {transform:scaleX(1)} 40%,52% {transform:scaleX(1.11)} 60% {transform:scaleX(.08)} 68% {transform:scaleX(1)} }
34
+ @keyframes ek-hit { 0%,60%,100% {opacity:0;transform:scale(.4)} 68% {opacity:.8;transform:scale(.7)} 88% {opacity:0;transform:scale(1.7)} }
35
+ /* Loader: walking toward the viewer, a 1.2 s stride (two steps). Ground rungs recede on an ease-out, as perspective slows them near the horizon. */
36
+ .ek-mascot--loading .ek-mascot__walker { animation:ek-sway 1.2s ease-in-out infinite; transform-origin:95px 185px; }
37
+ .ek-mascot--loading .ek-mascot__torso { animation:ek-bob .6s ease-in-out infinite; }
38
+ .ek-mascot--loading .ek-mascot__leg, .ek-mascot--loading .ek-mascot__hand { animation:ek-step 1.2s ease-in-out infinite; }
39
+ .ek-mascot--loading .ek-mascot__leg--r, .ek-mascot--loading .ek-mascot__hand--arrow { animation-delay:-.6s; }
40
+ .ek-mascot--loading .ek-mascot__shadow { animation:ek-shadow .6s ease-in-out infinite; transform-origin:95px 186px; }
41
+ .ek-mascot--loading .ek-mascot__rung { animation:ek-recede 1.2s cubic-bezier(.15,.6,.4,1) infinite; transform-origin:95px 162px; }
42
+ .ek-mascot--loading .ek-mascot__rung:nth-child(3) { animation-delay:-.3s; }
43
+ .ek-mascot--loading .ek-mascot__rung:nth-child(4) { animation-delay:-.6s; }
44
+ .ek-mascot--loading .ek-mascot__rung:nth-child(5) { animation-delay:-.9s; }
45
+ @keyframes ek-sway { 0%,100% {transform:rotate(-1.5deg)} 50% {transform:rotate(1.5deg)} }
46
+ @keyframes ek-bob { 0%,100% {transform:translateY(1px)} 50% {transform:translateY(-3px)} }
47
+ @keyframes ek-step { 0%,50%,100% {transform:translateY(0)} 25% {transform:translateY(-6px)} }
48
+ @keyframes ek-shadow { 0%,100% {transform:scaleX(1)} 50% {transform:scaleX(.92)} }
49
+ @keyframes ek-recede { 0% {transform:translateY(56px) scaleX(1);opacity:0} 10% {opacity:1} 85% {opacity:1} 100% {transform:translateY(0) scaleX(.4);opacity:0} }
34
50
  @media (prefers-reduced-motion:reduce) { .ek-mascot * {animation:none !important;transition:none !important} }
35
51
 
36
52
  .ek-companion--rail { padding:12px 20px; text-decoration:none; }
@@ -18,19 +18,35 @@ eklavya-mascot { display:inline-flex; flex-shrink:0; vertical-align:middle; line
18
18
  .ek-mascot { display:block; overflow:visible; flex-shrink:0; }
19
19
  .ek-mascot[data-variant="face"] { overflow:hidden; }
20
20
  .ek-mascot__face path:not([fill]) { fill:none; }
21
- .ek-mascot--loading .ek-mascot__character { animation:ek-breathe 2.8s ease-in-out infinite; transform-origin:95px 185px; }
22
- .ek-mascot--loading .ek-mascot__arrow { animation:ek-release 2.8s cubic-bezier(.3,0,.3,1) infinite; }
23
- .ek-mascot--loading .ek-mascot__string { animation:ek-draw 2.8s ease-in-out infinite; transform-origin:157px 125px; }
24
- .ek-mascot--loading .ek-mascot__ripple { animation:ek-hit 2.8s ease-out infinite; transform-origin:196px 128px; opacity:0; }
25
- @keyframes ek-breathe { 0%,100% {transform:translateY(0)} 35%,55% {transform:translateY(-3px) rotate(-1deg)} }
26
- @keyframes ek-release { 0%,12% {transform:translateX(0);opacity:1} 40%,52% {transform:translateX(-10px);opacity:1} 68% {transform:translateX(22px);opacity:0} 69%,85% {transform:translateX(0);opacity:0} 100% {transform:translateX(0);opacity:1} }
27
- @keyframes ek-draw { 0%,100% {transform:scaleX(1)} 40%,52% {transform:scaleX(1.11)} 60% {transform:scaleX(.08)} 68% {transform:scaleX(1)} }
28
- @keyframes ek-hit { 0%,60%,100% {opacity:0;transform:scale(.4)} 68% {opacity:.8;transform:scale(.7)} 88% {opacity:0;transform:scale(1.7)} }
29
21
  .ek-companion { display:flex;align-items:center;gap:12px;color:var(--dim);font:13px/1.5 var(--font-body); }
30
22
  .ek-companion--hero { margin-bottom:16px; }
31
23
  .ek-companion--hero b { display:block;color:var(--ink);font-weight:600; }
32
24
  .ek-mascot-message { display:flex;align-items:center;gap:16px;padding:16px 0; }
33
25
  .ek-mascot-message p {margin:0}
26
+ /* Secondary loader: draws and releases an arrow over 2.8 s. */
27
+ .ek-mascot--draw .ek-mascot__character { animation:ek-breathe 2.8s ease-in-out infinite; transform-origin:95px 185px; }
28
+ .ek-mascot--draw .ek-mascot__arrow { animation:ek-release 2.8s cubic-bezier(.3,0,.3,1) infinite; }
29
+ .ek-mascot--draw .ek-mascot__string { animation:ek-draw 2.8s ease-in-out infinite; transform-origin:157px 125px; }
30
+ .ek-mascot--draw .ek-mascot__ripple { animation:ek-hit 2.8s ease-out infinite; transform-origin:196px 128px; opacity:0; }
31
+ @keyframes ek-breathe { 0%,100% {transform:translateY(0)} 35%,55% {transform:translateY(-3px) rotate(-1deg)} }
32
+ @keyframes ek-release { 0%,12% {transform:translateX(0);opacity:1} 40%,52% {transform:translateX(-10px);opacity:1} 68% {transform:translateX(22px);opacity:0} 69%,85% {transform:translateX(0);opacity:0} 100% {transform:translateX(0);opacity:1} }
33
+ @keyframes ek-draw { 0%,100% {transform:scaleX(1)} 40%,52% {transform:scaleX(1.11)} 60% {transform:scaleX(.08)} 68% {transform:scaleX(1)} }
34
+ @keyframes ek-hit { 0%,60%,100% {opacity:0;transform:scale(.4)} 68% {opacity:.8;transform:scale(.7)} 88% {opacity:0;transform:scale(1.7)} }
35
+ /* Loader: walking toward the viewer, a 1.2 s stride (two steps). Ground rungs recede on an ease-out, as perspective slows them near the horizon. */
36
+ .ek-mascot--loading .ek-mascot__walker { animation:ek-sway 1.2s ease-in-out infinite; transform-origin:95px 185px; }
37
+ .ek-mascot--loading .ek-mascot__torso { animation:ek-bob .6s ease-in-out infinite; }
38
+ .ek-mascot--loading .ek-mascot__leg, .ek-mascot--loading .ek-mascot__hand { animation:ek-step 1.2s ease-in-out infinite; }
39
+ .ek-mascot--loading .ek-mascot__leg--r, .ek-mascot--loading .ek-mascot__hand--arrow { animation-delay:-.6s; }
40
+ .ek-mascot--loading .ek-mascot__shadow { animation:ek-shadow .6s ease-in-out infinite; transform-origin:95px 186px; }
41
+ .ek-mascot--loading .ek-mascot__rung { animation:ek-recede 1.2s cubic-bezier(.15,.6,.4,1) infinite; transform-origin:95px 162px; }
42
+ .ek-mascot--loading .ek-mascot__rung:nth-child(3) { animation-delay:-.3s; }
43
+ .ek-mascot--loading .ek-mascot__rung:nth-child(4) { animation-delay:-.6s; }
44
+ .ek-mascot--loading .ek-mascot__rung:nth-child(5) { animation-delay:-.9s; }
45
+ @keyframes ek-sway { 0%,100% {transform:rotate(-1.5deg)} 50% {transform:rotate(1.5deg)} }
46
+ @keyframes ek-bob { 0%,100% {transform:translateY(1px)} 50% {transform:translateY(-3px)} }
47
+ @keyframes ek-step { 0%,50%,100% {transform:translateY(0)} 25% {transform:translateY(-6px)} }
48
+ @keyframes ek-shadow { 0%,100% {transform:scaleX(1)} 50% {transform:scaleX(.92)} }
49
+ @keyframes ek-recede { 0% {transform:translateY(56px) scaleX(1);opacity:0} 10% {opacity:1} 85% {opacity:1} 100% {transform:translateY(0) scaleX(.4);opacity:0} }
34
50
  @media (prefers-reduced-motion:reduce) { .ek-mascot * {animation:none !important;transition:none !important} }
35
51
 
36
52
  .ek-companion--rail { padding:12px 20px; text-decoration:none; }
@@ -18,19 +18,35 @@ eklavya-mascot { display:inline-flex; flex-shrink:0; vertical-align:middle; line
18
18
  .ek-mascot { display:block; overflow:visible; flex-shrink:0; }
19
19
  .ek-mascot[data-variant="face"] { overflow:hidden; }
20
20
  .ek-mascot__face path:not([fill]) { fill:none; }
21
- .ek-mascot--loading .ek-mascot__character { animation:ek-breathe 2.8s ease-in-out infinite; transform-origin:95px 185px; }
22
- .ek-mascot--loading .ek-mascot__arrow { animation:ek-release 2.8s cubic-bezier(.3,0,.3,1) infinite; }
23
- .ek-mascot--loading .ek-mascot__string { animation:ek-draw 2.8s ease-in-out infinite; transform-origin:157px 125px; }
24
- .ek-mascot--loading .ek-mascot__ripple { animation:ek-hit 2.8s ease-out infinite; transform-origin:196px 128px; opacity:0; }
25
- @keyframes ek-breathe { 0%,100% {transform:translateY(0)} 35%,55% {transform:translateY(-3px) rotate(-1deg)} }
26
- @keyframes ek-release { 0%,12% {transform:translateX(0);opacity:1} 40%,52% {transform:translateX(-10px);opacity:1} 68% {transform:translateX(22px);opacity:0} 69%,85% {transform:translateX(0);opacity:0} 100% {transform:translateX(0);opacity:1} }
27
- @keyframes ek-draw { 0%,100% {transform:scaleX(1)} 40%,52% {transform:scaleX(1.11)} 60% {transform:scaleX(.08)} 68% {transform:scaleX(1)} }
28
- @keyframes ek-hit { 0%,60%,100% {opacity:0;transform:scale(.4)} 68% {opacity:.8;transform:scale(.7)} 88% {opacity:0;transform:scale(1.7)} }
29
21
  .ek-companion { display:flex;align-items:center;gap:12px;color:var(--dim);font:13px/1.5 var(--font-body); }
30
22
  .ek-companion--hero { margin-bottom:16px; }
31
23
  .ek-companion--hero b { display:block;color:var(--ink);font-weight:600; }
32
24
  .ek-mascot-message { display:flex;align-items:center;gap:16px;padding:16px 0; }
33
25
  .ek-mascot-message p {margin:0}
26
+ /* Secondary loader: draws and releases an arrow over 2.8 s. */
27
+ .ek-mascot--draw .ek-mascot__character { animation:ek-breathe 2.8s ease-in-out infinite; transform-origin:95px 185px; }
28
+ .ek-mascot--draw .ek-mascot__arrow { animation:ek-release 2.8s cubic-bezier(.3,0,.3,1) infinite; }
29
+ .ek-mascot--draw .ek-mascot__string { animation:ek-draw 2.8s ease-in-out infinite; transform-origin:157px 125px; }
30
+ .ek-mascot--draw .ek-mascot__ripple { animation:ek-hit 2.8s ease-out infinite; transform-origin:196px 128px; opacity:0; }
31
+ @keyframes ek-breathe { 0%,100% {transform:translateY(0)} 35%,55% {transform:translateY(-3px) rotate(-1deg)} }
32
+ @keyframes ek-release { 0%,12% {transform:translateX(0);opacity:1} 40%,52% {transform:translateX(-10px);opacity:1} 68% {transform:translateX(22px);opacity:0} 69%,85% {transform:translateX(0);opacity:0} 100% {transform:translateX(0);opacity:1} }
33
+ @keyframes ek-draw { 0%,100% {transform:scaleX(1)} 40%,52% {transform:scaleX(1.11)} 60% {transform:scaleX(.08)} 68% {transform:scaleX(1)} }
34
+ @keyframes ek-hit { 0%,60%,100% {opacity:0;transform:scale(.4)} 68% {opacity:.8;transform:scale(.7)} 88% {opacity:0;transform:scale(1.7)} }
35
+ /* Loader: walking toward the viewer, a 1.2 s stride (two steps). Ground rungs recede on an ease-out, as perspective slows them near the horizon. */
36
+ .ek-mascot--loading .ek-mascot__walker { animation:ek-sway 1.2s ease-in-out infinite; transform-origin:95px 185px; }
37
+ .ek-mascot--loading .ek-mascot__torso { animation:ek-bob .6s ease-in-out infinite; }
38
+ .ek-mascot--loading .ek-mascot__leg, .ek-mascot--loading .ek-mascot__hand { animation:ek-step 1.2s ease-in-out infinite; }
39
+ .ek-mascot--loading .ek-mascot__leg--r, .ek-mascot--loading .ek-mascot__hand--arrow { animation-delay:-.6s; }
40
+ .ek-mascot--loading .ek-mascot__shadow { animation:ek-shadow .6s ease-in-out infinite; transform-origin:95px 186px; }
41
+ .ek-mascot--loading .ek-mascot__rung { animation:ek-recede 1.2s cubic-bezier(.15,.6,.4,1) infinite; transform-origin:95px 162px; }
42
+ .ek-mascot--loading .ek-mascot__rung:nth-child(3) { animation-delay:-.3s; }
43
+ .ek-mascot--loading .ek-mascot__rung:nth-child(4) { animation-delay:-.6s; }
44
+ .ek-mascot--loading .ek-mascot__rung:nth-child(5) { animation-delay:-.9s; }
45
+ @keyframes ek-sway { 0%,100% {transform:rotate(-1.5deg)} 50% {transform:rotate(1.5deg)} }
46
+ @keyframes ek-bob { 0%,100% {transform:translateY(1px)} 50% {transform:translateY(-3px)} }
47
+ @keyframes ek-step { 0%,50%,100% {transform:translateY(0)} 25% {transform:translateY(-6px)} }
48
+ @keyframes ek-shadow { 0%,100% {transform:scaleX(1)} 50% {transform:scaleX(.92)} }
49
+ @keyframes ek-recede { 0% {transform:translateY(56px) scaleX(1);opacity:0} 10% {opacity:1} 85% {opacity:1} 100% {transform:translateY(0) scaleX(.4);opacity:0} }
34
50
  @media (prefers-reduced-motion:reduce) { .ek-mascot * {animation:none !important;transition:none !important} }
35
51
 
36
52
  .ek-companion--rail { padding:12px 20px; text-decoration:none; }
@@ -18,19 +18,35 @@ eklavya-mascot { display:inline-flex; flex-shrink:0; vertical-align:middle; line
18
18
  .ek-mascot { display:block; overflow:visible; flex-shrink:0; }
19
19
  .ek-mascot[data-variant="face"] { overflow:hidden; }
20
20
  .ek-mascot__face path:not([fill]) { fill:none; }
21
- .ek-mascot--loading .ek-mascot__character { animation:ek-breathe 2.8s ease-in-out infinite; transform-origin:95px 185px; }
22
- .ek-mascot--loading .ek-mascot__arrow { animation:ek-release 2.8s cubic-bezier(.3,0,.3,1) infinite; }
23
- .ek-mascot--loading .ek-mascot__string { animation:ek-draw 2.8s ease-in-out infinite; transform-origin:157px 125px; }
24
- .ek-mascot--loading .ek-mascot__ripple { animation:ek-hit 2.8s ease-out infinite; transform-origin:196px 128px; opacity:0; }
25
- @keyframes ek-breathe { 0%,100% {transform:translateY(0)} 35%,55% {transform:translateY(-3px) rotate(-1deg)} }
26
- @keyframes ek-release { 0%,12% {transform:translateX(0);opacity:1} 40%,52% {transform:translateX(-10px);opacity:1} 68% {transform:translateX(22px);opacity:0} 69%,85% {transform:translateX(0);opacity:0} 100% {transform:translateX(0);opacity:1} }
27
- @keyframes ek-draw { 0%,100% {transform:scaleX(1)} 40%,52% {transform:scaleX(1.11)} 60% {transform:scaleX(.08)} 68% {transform:scaleX(1)} }
28
- @keyframes ek-hit { 0%,60%,100% {opacity:0;transform:scale(.4)} 68% {opacity:.8;transform:scale(.7)} 88% {opacity:0;transform:scale(1.7)} }
29
21
  .ek-companion { display:flex;align-items:center;gap:12px;color:var(--dim);font:13px/1.5 var(--font-body); }
30
22
  .ek-companion--hero { margin-bottom:16px; }
31
23
  .ek-companion--hero b { display:block;color:var(--ink);font-weight:600; }
32
24
  .ek-mascot-message { display:flex;align-items:center;gap:16px;padding:16px 0; }
33
25
  .ek-mascot-message p {margin:0}
26
+ /* Secondary loader: draws and releases an arrow over 2.8 s. */
27
+ .ek-mascot--draw .ek-mascot__character { animation:ek-breathe 2.8s ease-in-out infinite; transform-origin:95px 185px; }
28
+ .ek-mascot--draw .ek-mascot__arrow { animation:ek-release 2.8s cubic-bezier(.3,0,.3,1) infinite; }
29
+ .ek-mascot--draw .ek-mascot__string { animation:ek-draw 2.8s ease-in-out infinite; transform-origin:157px 125px; }
30
+ .ek-mascot--draw .ek-mascot__ripple { animation:ek-hit 2.8s ease-out infinite; transform-origin:196px 128px; opacity:0; }
31
+ @keyframes ek-breathe { 0%,100% {transform:translateY(0)} 35%,55% {transform:translateY(-3px) rotate(-1deg)} }
32
+ @keyframes ek-release { 0%,12% {transform:translateX(0);opacity:1} 40%,52% {transform:translateX(-10px);opacity:1} 68% {transform:translateX(22px);opacity:0} 69%,85% {transform:translateX(0);opacity:0} 100% {transform:translateX(0);opacity:1} }
33
+ @keyframes ek-draw { 0%,100% {transform:scaleX(1)} 40%,52% {transform:scaleX(1.11)} 60% {transform:scaleX(.08)} 68% {transform:scaleX(1)} }
34
+ @keyframes ek-hit { 0%,60%,100% {opacity:0;transform:scale(.4)} 68% {opacity:.8;transform:scale(.7)} 88% {opacity:0;transform:scale(1.7)} }
35
+ /* Loader: walking toward the viewer, a 1.2 s stride (two steps). Ground rungs recede on an ease-out, as perspective slows them near the horizon. */
36
+ .ek-mascot--loading .ek-mascot__walker { animation:ek-sway 1.2s ease-in-out infinite; transform-origin:95px 185px; }
37
+ .ek-mascot--loading .ek-mascot__torso { animation:ek-bob .6s ease-in-out infinite; }
38
+ .ek-mascot--loading .ek-mascot__leg, .ek-mascot--loading .ek-mascot__hand { animation:ek-step 1.2s ease-in-out infinite; }
39
+ .ek-mascot--loading .ek-mascot__leg--r, .ek-mascot--loading .ek-mascot__hand--arrow { animation-delay:-.6s; }
40
+ .ek-mascot--loading .ek-mascot__shadow { animation:ek-shadow .6s ease-in-out infinite; transform-origin:95px 186px; }
41
+ .ek-mascot--loading .ek-mascot__rung { animation:ek-recede 1.2s cubic-bezier(.15,.6,.4,1) infinite; transform-origin:95px 162px; }
42
+ .ek-mascot--loading .ek-mascot__rung:nth-child(3) { animation-delay:-.3s; }
43
+ .ek-mascot--loading .ek-mascot__rung:nth-child(4) { animation-delay:-.6s; }
44
+ .ek-mascot--loading .ek-mascot__rung:nth-child(5) { animation-delay:-.9s; }
45
+ @keyframes ek-sway { 0%,100% {transform:rotate(-1.5deg)} 50% {transform:rotate(1.5deg)} }
46
+ @keyframes ek-bob { 0%,100% {transform:translateY(1px)} 50% {transform:translateY(-3px)} }
47
+ @keyframes ek-step { 0%,50%,100% {transform:translateY(0)} 25% {transform:translateY(-6px)} }
48
+ @keyframes ek-shadow { 0%,100% {transform:scaleX(1)} 50% {transform:scaleX(.92)} }
49
+ @keyframes ek-recede { 0% {transform:translateY(56px) scaleX(1);opacity:0} 10% {opacity:1} 85% {opacity:1} 100% {transform:translateY(0) scaleX(.4);opacity:0} }
34
50
  @media (prefers-reduced-motion:reduce) { .ek-mascot * {animation:none !important;transition:none !important} }
35
51
 
36
52
  .ek-companion--rail { padding:12px 20px; text-decoration:none; }
@@ -0,0 +1,85 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 210 200" width="200" height="200" class="ek-mascot" data-expression="cool" data-variant="body" role="img"><style>:root {/* ---------- MASCOT IDENTITY ----------
2
+ Archer Cloak artwork keeps its approved identity across both grounds.
3
+ UI chrome continues to use the surface, text and accent roles below. */
4
+ --mascot-coral: #ef9875;
5
+ --mascot-coral-dark: #c96d51;
6
+ --mascot-highlight: #ffc8a6;
7
+ --mascot-cloak: #0E6E66;
8
+ --mascot-cloak-dark: #07403C;
9
+ --mascot-thread: #C4EDE4;
10
+ --mascot-ink: #302c2a;
11
+ --mascot-metal: #5b7c73;
12
+ --mascot-wood: #bc9569;
13
+ --mascot-wood-dark: #74583f;
14
+ --mascot-string: #EAE7E1;
15
+ --mascot-shadow: #26262a;
16
+ /* ---------- END MASCOT IDENTITY ---------- */}/* Identity colours and UI surfaces use Eklavya's semantic tokens. */
17
+ eklavya-mascot { display:inline-flex; flex-shrink:0; vertical-align:middle; line-height:0; }
18
+ .ek-mascot { display:block; overflow:visible; flex-shrink:0; }
19
+ .ek-mascot[data-variant="face"] { overflow:hidden; }
20
+ .ek-mascot__face path:not([fill]) { fill:none; }
21
+ .ek-companion { display:flex;align-items:center;gap:12px;color:var(--dim);font:13px/1.5 var(--font-body); }
22
+ .ek-companion--hero { margin-bottom:16px; }
23
+ .ek-companion--hero b { display:block;color:var(--ink);font-weight:600; }
24
+ .ek-mascot-message { display:flex;align-items:center;gap:16px;padding:16px 0; }
25
+ .ek-mascot-message p {margin:0}
26
+ /* Secondary loader: draws and releases an arrow over 2.8 s. */
27
+ .ek-mascot--draw .ek-mascot__character { animation:ek-breathe 2.8s ease-in-out infinite; transform-origin:95px 185px; }
28
+ .ek-mascot--draw .ek-mascot__arrow { animation:ek-release 2.8s cubic-bezier(.3,0,.3,1) infinite; }
29
+ .ek-mascot--draw .ek-mascot__string { animation:ek-draw 2.8s ease-in-out infinite; transform-origin:157px 125px; }
30
+ .ek-mascot--draw .ek-mascot__ripple { animation:ek-hit 2.8s ease-out infinite; transform-origin:196px 128px; opacity:0; }
31
+ @keyframes ek-breathe { 0%,100% {transform:translateY(0)} 35%,55% {transform:translateY(-3px) rotate(-1deg)} }
32
+ @keyframes ek-release { 0%,12% {transform:translateX(0);opacity:1} 40%,52% {transform:translateX(-10px);opacity:1} 68% {transform:translateX(22px);opacity:0} 69%,85% {transform:translateX(0);opacity:0} 100% {transform:translateX(0);opacity:1} }
33
+ @keyframes ek-draw { 0%,100% {transform:scaleX(1)} 40%,52% {transform:scaleX(1.11)} 60% {transform:scaleX(.08)} 68% {transform:scaleX(1)} }
34
+ @keyframes ek-hit { 0%,60%,100% {opacity:0;transform:scale(.4)} 68% {opacity:.8;transform:scale(.7)} 88% {opacity:0;transform:scale(1.7)} }
35
+ /* Loader: walking toward the viewer, a 1.2 s stride (two steps). Ground rungs recede on an ease-out, as perspective slows them near the horizon. */
36
+ .ek-mascot--loading .ek-mascot__walker { animation:ek-sway 1.2s ease-in-out infinite; transform-origin:95px 185px; }
37
+ .ek-mascot--loading .ek-mascot__torso { animation:ek-bob .6s ease-in-out infinite; }
38
+ .ek-mascot--loading .ek-mascot__leg, .ek-mascot--loading .ek-mascot__hand { animation:ek-step 1.2s ease-in-out infinite; }
39
+ .ek-mascot--loading .ek-mascot__leg--r, .ek-mascot--loading .ek-mascot__hand--arrow { animation-delay:-.6s; }
40
+ .ek-mascot--loading .ek-mascot__shadow { animation:ek-shadow .6s ease-in-out infinite; transform-origin:95px 186px; }
41
+ .ek-mascot--loading .ek-mascot__rung { animation:ek-recede 1.2s cubic-bezier(.15,.6,.4,1) infinite; transform-origin:95px 162px; }
42
+ .ek-mascot--loading .ek-mascot__rung:nth-child(3) { animation-delay:-.3s; }
43
+ .ek-mascot--loading .ek-mascot__rung:nth-child(4) { animation-delay:-.6s; }
44
+ .ek-mascot--loading .ek-mascot__rung:nth-child(5) { animation-delay:-.9s; }
45
+ @keyframes ek-sway { 0%,100% {transform:rotate(-1.5deg)} 50% {transform:rotate(1.5deg)} }
46
+ @keyframes ek-bob { 0%,100% {transform:translateY(1px)} 50% {transform:translateY(-3px)} }
47
+ @keyframes ek-step { 0%,50%,100% {transform:translateY(0)} 25% {transform:translateY(-6px)} }
48
+ @keyframes ek-shadow { 0%,100% {transform:scaleX(1)} 50% {transform:scaleX(.92)} }
49
+ @keyframes ek-recede { 0% {transform:translateY(56px) scaleX(1);opacity:0} 10% {opacity:1} 85% {opacity:1} 100% {transform:translateY(0) scaleX(.4);opacity:0} }
50
+ @media (prefers-reduced-motion:reduce) { .ek-mascot * {animation:none !important;transition:none !important} }
51
+
52
+ .ek-companion--rail { padding:12px 20px; text-decoration:none; }
53
+ .ek-mascot-message > :last-child { min-width:0; overflow-wrap:break-word; }
54
+ .ek-mascot-status { display:inline-flex; align-items:center; gap:6px; }
55
+ </style><title>Eklavya, cool and confident</title><g class="ek-mascot__character">
56
+ <ellipse cx="94" cy="185" rx="49" ry="5" fill="var(--mascot-shadow)"/>
57
+ <path d="M51 124Q29 137 34 168l23-8 17-27m59-9q26 8 21 39l-24-7-17-22" fill="var(--mascot-cloak-dark)"/>
58
+ <path d="M65 157v23q0 8 16 6l4-28m21 0 1 23q2 8 17 4v-29" fill="var(--mascot-coral)"/>
59
+ <path d="M58 122q35-16 72 0l8 46q-19 9-42 1-23 7-43-1Z" fill="var(--mascot-cloak)"/>
60
+ <path d="M96 127v40m-31-22q29 9 64 0" fill="none" stroke="var(--mascot-cloak-dark)" stroke-width="3"/>
61
+ <path d="M52 109q42-22 86 0l-9 29-32-14-32 14Z" fill="var(--mascot-cloak)" stroke="var(--mascot-cloak-dark)" stroke-width="3" stroke-linejoin="round"/>
62
+ <path d="M57 114l37 10m42-10-39 10" fill="none" stroke="var(--mascot-thread)" stroke-width="2"/>
63
+ <path d="M54 148h80v11H54Z" fill="var(--mascot-ink)"/>
64
+ <rect x="87" y="146" width="17" height="15" rx="3" fill="var(--mascot-metal)"/><rect x="92" y="150" width="7" height="7" rx="1" fill="var(--mascot-ink)"/>
65
+ <g class="ek-mascot__head">
66
+ <path d="M46 43h18V29h19V18h23v12h20v14h17v19h8v29h-8v16q0 12-16 15H62q-18-3-18-17V91h-9V63h11Z" fill="var(--mascot-coral)" stroke="var(--mascot-coral-dark)" stroke-width="2" stroke-linejoin="round"/>
67
+ <path d="M48 45h18V32h18V21h19" fill="none" stroke="var(--mascot-highlight)" stroke-width="3" stroke-linejoin="round"/>
68
+ <g class="ek-mascot__face" fill="var(--mascot-ink)" stroke="var(--mascot-ink)" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><path d="M60 65h19v7q0 9-9 9h-1q-9 0-9-9Zm46 0h19v7q0 9-9 9h-1q-9 0-9-9Z" fill="var(--mascot-ink)"/><path d="M56 66h72M86 92q9 3 15-4"/></g>
69
+ </g>
70
+ <circle cx="96" cy="125" r="7" fill="var(--mascot-metal)"/><circle cx="94" cy="123" r="2" fill="var(--mascot-thread)"/>
71
+ <g class="ek-mascot__bow" fill="none" stroke-linecap="round" stroke-linejoin="round">
72
+ <path d="M157 78q36 47 0 94" stroke="var(--mascot-wood-dark)" stroke-width="8"/>
73
+ <path d="M158 78q32 47 0 94" stroke="var(--mascot-wood)" stroke-width="4"/>
74
+ <path class="ek-mascot__string" d="M157 78l-88 50 88 44" stroke="var(--mascot-string)" stroke-width="1.8"/>
75
+ </g>
76
+ <path d="M130 127l16-8q10-1 13 5 2 8-6 11l-15 4" fill="var(--mascot-cloak)"/>
77
+ <rect x="147" y="117" width="15" height="18" rx="7" fill="var(--mascot-coral)"/>
78
+ <g class="ek-mascot__arrow" stroke-linecap="round" stroke-linejoin="round">
79
+ <path d="M69 128h114" stroke="var(--mascot-wood-dark)" stroke-width="3"/>
80
+ <path d="M179 120l13 8-13 8Z" fill="var(--mascot-thread)" stroke="var(--mascot-wood)" stroke-width="1.5"/>
81
+ <path d="M71 128l-9-9 10 1 8 8-8 8-10 1Z" fill="var(--mascot-thread)"/>
82
+ </g>
83
+ <path d="M58 128q-11-6-14 5-2 9 8 13l17-8" fill="var(--mascot-cloak)"/>
84
+ <rect x="62" y="121" width="18" height="16" rx="7" fill="var(--mascot-coral)"/>
85
+ </g></svg>
@@ -0,0 +1,59 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="30 12 126 128" width="200" height="200" class="ek-mascot" data-expression="cool" data-variant="face" role="img"><style>:root {/* ---------- MASCOT IDENTITY ----------
2
+ Archer Cloak artwork keeps its approved identity across both grounds.
3
+ UI chrome continues to use the surface, text and accent roles below. */
4
+ --mascot-coral: #ef9875;
5
+ --mascot-coral-dark: #c96d51;
6
+ --mascot-highlight: #ffc8a6;
7
+ --mascot-cloak: #0E6E66;
8
+ --mascot-cloak-dark: #07403C;
9
+ --mascot-thread: #C4EDE4;
10
+ --mascot-ink: #302c2a;
11
+ --mascot-metal: #5b7c73;
12
+ --mascot-wood: #bc9569;
13
+ --mascot-wood-dark: #74583f;
14
+ --mascot-string: #EAE7E1;
15
+ --mascot-shadow: #26262a;
16
+ /* ---------- END MASCOT IDENTITY ---------- */}/* Identity colours and UI surfaces use Eklavya's semantic tokens. */
17
+ eklavya-mascot { display:inline-flex; flex-shrink:0; vertical-align:middle; line-height:0; }
18
+ .ek-mascot { display:block; overflow:visible; flex-shrink:0; }
19
+ .ek-mascot[data-variant="face"] { overflow:hidden; }
20
+ .ek-mascot__face path:not([fill]) { fill:none; }
21
+ .ek-companion { display:flex;align-items:center;gap:12px;color:var(--dim);font:13px/1.5 var(--font-body); }
22
+ .ek-companion--hero { margin-bottom:16px; }
23
+ .ek-companion--hero b { display:block;color:var(--ink);font-weight:600; }
24
+ .ek-mascot-message { display:flex;align-items:center;gap:16px;padding:16px 0; }
25
+ .ek-mascot-message p {margin:0}
26
+ /* Secondary loader: draws and releases an arrow over 2.8 s. */
27
+ .ek-mascot--draw .ek-mascot__character { animation:ek-breathe 2.8s ease-in-out infinite; transform-origin:95px 185px; }
28
+ .ek-mascot--draw .ek-mascot__arrow { animation:ek-release 2.8s cubic-bezier(.3,0,.3,1) infinite; }
29
+ .ek-mascot--draw .ek-mascot__string { animation:ek-draw 2.8s ease-in-out infinite; transform-origin:157px 125px; }
30
+ .ek-mascot--draw .ek-mascot__ripple { animation:ek-hit 2.8s ease-out infinite; transform-origin:196px 128px; opacity:0; }
31
+ @keyframes ek-breathe { 0%,100% {transform:translateY(0)} 35%,55% {transform:translateY(-3px) rotate(-1deg)} }
32
+ @keyframes ek-release { 0%,12% {transform:translateX(0);opacity:1} 40%,52% {transform:translateX(-10px);opacity:1} 68% {transform:translateX(22px);opacity:0} 69%,85% {transform:translateX(0);opacity:0} 100% {transform:translateX(0);opacity:1} }
33
+ @keyframes ek-draw { 0%,100% {transform:scaleX(1)} 40%,52% {transform:scaleX(1.11)} 60% {transform:scaleX(.08)} 68% {transform:scaleX(1)} }
34
+ @keyframes ek-hit { 0%,60%,100% {opacity:0;transform:scale(.4)} 68% {opacity:.8;transform:scale(.7)} 88% {opacity:0;transform:scale(1.7)} }
35
+ /* Loader: walking toward the viewer, a 1.2 s stride (two steps). Ground rungs recede on an ease-out, as perspective slows them near the horizon. */
36
+ .ek-mascot--loading .ek-mascot__walker { animation:ek-sway 1.2s ease-in-out infinite; transform-origin:95px 185px; }
37
+ .ek-mascot--loading .ek-mascot__torso { animation:ek-bob .6s ease-in-out infinite; }
38
+ .ek-mascot--loading .ek-mascot__leg, .ek-mascot--loading .ek-mascot__hand { animation:ek-step 1.2s ease-in-out infinite; }
39
+ .ek-mascot--loading .ek-mascot__leg--r, .ek-mascot--loading .ek-mascot__hand--arrow { animation-delay:-.6s; }
40
+ .ek-mascot--loading .ek-mascot__shadow { animation:ek-shadow .6s ease-in-out infinite; transform-origin:95px 186px; }
41
+ .ek-mascot--loading .ek-mascot__rung { animation:ek-recede 1.2s cubic-bezier(.15,.6,.4,1) infinite; transform-origin:95px 162px; }
42
+ .ek-mascot--loading .ek-mascot__rung:nth-child(3) { animation-delay:-.3s; }
43
+ .ek-mascot--loading .ek-mascot__rung:nth-child(4) { animation-delay:-.6s; }
44
+ .ek-mascot--loading .ek-mascot__rung:nth-child(5) { animation-delay:-.9s; }
45
+ @keyframes ek-sway { 0%,100% {transform:rotate(-1.5deg)} 50% {transform:rotate(1.5deg)} }
46
+ @keyframes ek-bob { 0%,100% {transform:translateY(1px)} 50% {transform:translateY(-3px)} }
47
+ @keyframes ek-step { 0%,50%,100% {transform:translateY(0)} 25% {transform:translateY(-6px)} }
48
+ @keyframes ek-shadow { 0%,100% {transform:scaleX(1)} 50% {transform:scaleX(.92)} }
49
+ @keyframes ek-recede { 0% {transform:translateY(56px) scaleX(1);opacity:0} 10% {opacity:1} 85% {opacity:1} 100% {transform:translateY(0) scaleX(.4);opacity:0} }
50
+ @media (prefers-reduced-motion:reduce) { .ek-mascot * {animation:none !important;transition:none !important} }
51
+
52
+ .ek-companion--rail { padding:12px 20px; text-decoration:none; }
53
+ .ek-mascot-message > :last-child { min-width:0; overflow-wrap:break-word; }
54
+ .ek-mascot-status { display:inline-flex; align-items:center; gap:6px; }
55
+ </style><title>Eklavya, cool and confident</title><path d="M48 109q44-19 88 0l-8 30-33-15-31 15Z" fill="var(--mascot-cloak)"/><g class="ek-mascot__head">
56
+ <path d="M46 43h18V29h19V18h23v12h20v14h17v19h8v29h-8v16q0 12-16 15H62q-18-3-18-17V91h-9V63h11Z" fill="var(--mascot-coral)" stroke="var(--mascot-coral-dark)" stroke-width="2" stroke-linejoin="round"/>
57
+ <path d="M48 45h18V32h18V21h19" fill="none" stroke="var(--mascot-highlight)" stroke-width="3" stroke-linejoin="round"/>
58
+ <g class="ek-mascot__face" fill="var(--mascot-ink)" stroke="var(--mascot-ink)" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><path d="M60 65h19v7q0 9-9 9h-1q-9 0-9-9Zm46 0h19v7q0 9-9 9h-1q-9 0-9-9Z" fill="var(--mascot-ink)"/><path d="M56 66h72M86 92q9 3 15-4"/></g>
59
+ </g><circle cx="95" cy="126" r="6" fill="var(--mascot-metal)"/></svg>
@@ -18,19 +18,35 @@ eklavya-mascot { display:inline-flex; flex-shrink:0; vertical-align:middle; line
18
18
  .ek-mascot { display:block; overflow:visible; flex-shrink:0; }
19
19
  .ek-mascot[data-variant="face"] { overflow:hidden; }
20
20
  .ek-mascot__face path:not([fill]) { fill:none; }
21
- .ek-mascot--loading .ek-mascot__character { animation:ek-breathe 2.8s ease-in-out infinite; transform-origin:95px 185px; }
22
- .ek-mascot--loading .ek-mascot__arrow { animation:ek-release 2.8s cubic-bezier(.3,0,.3,1) infinite; }
23
- .ek-mascot--loading .ek-mascot__string { animation:ek-draw 2.8s ease-in-out infinite; transform-origin:157px 125px; }
24
- .ek-mascot--loading .ek-mascot__ripple { animation:ek-hit 2.8s ease-out infinite; transform-origin:196px 128px; opacity:0; }
25
- @keyframes ek-breathe { 0%,100% {transform:translateY(0)} 35%,55% {transform:translateY(-3px) rotate(-1deg)} }
26
- @keyframes ek-release { 0%,12% {transform:translateX(0);opacity:1} 40%,52% {transform:translateX(-10px);opacity:1} 68% {transform:translateX(22px);opacity:0} 69%,85% {transform:translateX(0);opacity:0} 100% {transform:translateX(0);opacity:1} }
27
- @keyframes ek-draw { 0%,100% {transform:scaleX(1)} 40%,52% {transform:scaleX(1.11)} 60% {transform:scaleX(.08)} 68% {transform:scaleX(1)} }
28
- @keyframes ek-hit { 0%,60%,100% {opacity:0;transform:scale(.4)} 68% {opacity:.8;transform:scale(.7)} 88% {opacity:0;transform:scale(1.7)} }
29
21
  .ek-companion { display:flex;align-items:center;gap:12px;color:var(--dim);font:13px/1.5 var(--font-body); }
30
22
  .ek-companion--hero { margin-bottom:16px; }
31
23
  .ek-companion--hero b { display:block;color:var(--ink);font-weight:600; }
32
24
  .ek-mascot-message { display:flex;align-items:center;gap:16px;padding:16px 0; }
33
25
  .ek-mascot-message p {margin:0}
26
+ /* Secondary loader: draws and releases an arrow over 2.8 s. */
27
+ .ek-mascot--draw .ek-mascot__character { animation:ek-breathe 2.8s ease-in-out infinite; transform-origin:95px 185px; }
28
+ .ek-mascot--draw .ek-mascot__arrow { animation:ek-release 2.8s cubic-bezier(.3,0,.3,1) infinite; }
29
+ .ek-mascot--draw .ek-mascot__string { animation:ek-draw 2.8s ease-in-out infinite; transform-origin:157px 125px; }
30
+ .ek-mascot--draw .ek-mascot__ripple { animation:ek-hit 2.8s ease-out infinite; transform-origin:196px 128px; opacity:0; }
31
+ @keyframes ek-breathe { 0%,100% {transform:translateY(0)} 35%,55% {transform:translateY(-3px) rotate(-1deg)} }
32
+ @keyframes ek-release { 0%,12% {transform:translateX(0);opacity:1} 40%,52% {transform:translateX(-10px);opacity:1} 68% {transform:translateX(22px);opacity:0} 69%,85% {transform:translateX(0);opacity:0} 100% {transform:translateX(0);opacity:1} }
33
+ @keyframes ek-draw { 0%,100% {transform:scaleX(1)} 40%,52% {transform:scaleX(1.11)} 60% {transform:scaleX(.08)} 68% {transform:scaleX(1)} }
34
+ @keyframes ek-hit { 0%,60%,100% {opacity:0;transform:scale(.4)} 68% {opacity:.8;transform:scale(.7)} 88% {opacity:0;transform:scale(1.7)} }
35
+ /* Loader: walking toward the viewer, a 1.2 s stride (two steps). Ground rungs recede on an ease-out, as perspective slows them near the horizon. */
36
+ .ek-mascot--loading .ek-mascot__walker { animation:ek-sway 1.2s ease-in-out infinite; transform-origin:95px 185px; }
37
+ .ek-mascot--loading .ek-mascot__torso { animation:ek-bob .6s ease-in-out infinite; }
38
+ .ek-mascot--loading .ek-mascot__leg, .ek-mascot--loading .ek-mascot__hand { animation:ek-step 1.2s ease-in-out infinite; }
39
+ .ek-mascot--loading .ek-mascot__leg--r, .ek-mascot--loading .ek-mascot__hand--arrow { animation-delay:-.6s; }
40
+ .ek-mascot--loading .ek-mascot__shadow { animation:ek-shadow .6s ease-in-out infinite; transform-origin:95px 186px; }
41
+ .ek-mascot--loading .ek-mascot__rung { animation:ek-recede 1.2s cubic-bezier(.15,.6,.4,1) infinite; transform-origin:95px 162px; }
42
+ .ek-mascot--loading .ek-mascot__rung:nth-child(3) { animation-delay:-.3s; }
43
+ .ek-mascot--loading .ek-mascot__rung:nth-child(4) { animation-delay:-.6s; }
44
+ .ek-mascot--loading .ek-mascot__rung:nth-child(5) { animation-delay:-.9s; }
45
+ @keyframes ek-sway { 0%,100% {transform:rotate(-1.5deg)} 50% {transform:rotate(1.5deg)} }
46
+ @keyframes ek-bob { 0%,100% {transform:translateY(1px)} 50% {transform:translateY(-3px)} }
47
+ @keyframes ek-step { 0%,50%,100% {transform:translateY(0)} 25% {transform:translateY(-6px)} }
48
+ @keyframes ek-shadow { 0%,100% {transform:scaleX(1)} 50% {transform:scaleX(.92)} }
49
+ @keyframes ek-recede { 0% {transform:translateY(56px) scaleX(1);opacity:0} 10% {opacity:1} 85% {opacity:1} 100% {transform:translateY(0) scaleX(.4);opacity:0} }
34
50
  @media (prefers-reduced-motion:reduce) { .ek-mascot * {animation:none !important;transition:none !important} }
35
51
 
36
52
  .ek-companion--rail { padding:12px 20px; text-decoration:none; }
@@ -18,19 +18,35 @@ eklavya-mascot { display:inline-flex; flex-shrink:0; vertical-align:middle; line
18
18
  .ek-mascot { display:block; overflow:visible; flex-shrink:0; }
19
19
  .ek-mascot[data-variant="face"] { overflow:hidden; }
20
20
  .ek-mascot__face path:not([fill]) { fill:none; }
21
- .ek-mascot--loading .ek-mascot__character { animation:ek-breathe 2.8s ease-in-out infinite; transform-origin:95px 185px; }
22
- .ek-mascot--loading .ek-mascot__arrow { animation:ek-release 2.8s cubic-bezier(.3,0,.3,1) infinite; }
23
- .ek-mascot--loading .ek-mascot__string { animation:ek-draw 2.8s ease-in-out infinite; transform-origin:157px 125px; }
24
- .ek-mascot--loading .ek-mascot__ripple { animation:ek-hit 2.8s ease-out infinite; transform-origin:196px 128px; opacity:0; }
25
- @keyframes ek-breathe { 0%,100% {transform:translateY(0)} 35%,55% {transform:translateY(-3px) rotate(-1deg)} }
26
- @keyframes ek-release { 0%,12% {transform:translateX(0);opacity:1} 40%,52% {transform:translateX(-10px);opacity:1} 68% {transform:translateX(22px);opacity:0} 69%,85% {transform:translateX(0);opacity:0} 100% {transform:translateX(0);opacity:1} }
27
- @keyframes ek-draw { 0%,100% {transform:scaleX(1)} 40%,52% {transform:scaleX(1.11)} 60% {transform:scaleX(.08)} 68% {transform:scaleX(1)} }
28
- @keyframes ek-hit { 0%,60%,100% {opacity:0;transform:scale(.4)} 68% {opacity:.8;transform:scale(.7)} 88% {opacity:0;transform:scale(1.7)} }
29
21
  .ek-companion { display:flex;align-items:center;gap:12px;color:var(--dim);font:13px/1.5 var(--font-body); }
30
22
  .ek-companion--hero { margin-bottom:16px; }
31
23
  .ek-companion--hero b { display:block;color:var(--ink);font-weight:600; }
32
24
  .ek-mascot-message { display:flex;align-items:center;gap:16px;padding:16px 0; }
33
25
  .ek-mascot-message p {margin:0}
26
+ /* Secondary loader: draws and releases an arrow over 2.8 s. */
27
+ .ek-mascot--draw .ek-mascot__character { animation:ek-breathe 2.8s ease-in-out infinite; transform-origin:95px 185px; }
28
+ .ek-mascot--draw .ek-mascot__arrow { animation:ek-release 2.8s cubic-bezier(.3,0,.3,1) infinite; }
29
+ .ek-mascot--draw .ek-mascot__string { animation:ek-draw 2.8s ease-in-out infinite; transform-origin:157px 125px; }
30
+ .ek-mascot--draw .ek-mascot__ripple { animation:ek-hit 2.8s ease-out infinite; transform-origin:196px 128px; opacity:0; }
31
+ @keyframes ek-breathe { 0%,100% {transform:translateY(0)} 35%,55% {transform:translateY(-3px) rotate(-1deg)} }
32
+ @keyframes ek-release { 0%,12% {transform:translateX(0);opacity:1} 40%,52% {transform:translateX(-10px);opacity:1} 68% {transform:translateX(22px);opacity:0} 69%,85% {transform:translateX(0);opacity:0} 100% {transform:translateX(0);opacity:1} }
33
+ @keyframes ek-draw { 0%,100% {transform:scaleX(1)} 40%,52% {transform:scaleX(1.11)} 60% {transform:scaleX(.08)} 68% {transform:scaleX(1)} }
34
+ @keyframes ek-hit { 0%,60%,100% {opacity:0;transform:scale(.4)} 68% {opacity:.8;transform:scale(.7)} 88% {opacity:0;transform:scale(1.7)} }
35
+ /* Loader: walking toward the viewer, a 1.2 s stride (two steps). Ground rungs recede on an ease-out, as perspective slows them near the horizon. */
36
+ .ek-mascot--loading .ek-mascot__walker { animation:ek-sway 1.2s ease-in-out infinite; transform-origin:95px 185px; }
37
+ .ek-mascot--loading .ek-mascot__torso { animation:ek-bob .6s ease-in-out infinite; }
38
+ .ek-mascot--loading .ek-mascot__leg, .ek-mascot--loading .ek-mascot__hand { animation:ek-step 1.2s ease-in-out infinite; }
39
+ .ek-mascot--loading .ek-mascot__leg--r, .ek-mascot--loading .ek-mascot__hand--arrow { animation-delay:-.6s; }
40
+ .ek-mascot--loading .ek-mascot__shadow { animation:ek-shadow .6s ease-in-out infinite; transform-origin:95px 186px; }
41
+ .ek-mascot--loading .ek-mascot__rung { animation:ek-recede 1.2s cubic-bezier(.15,.6,.4,1) infinite; transform-origin:95px 162px; }
42
+ .ek-mascot--loading .ek-mascot__rung:nth-child(3) { animation-delay:-.3s; }
43
+ .ek-mascot--loading .ek-mascot__rung:nth-child(4) { animation-delay:-.6s; }
44
+ .ek-mascot--loading .ek-mascot__rung:nth-child(5) { animation-delay:-.9s; }
45
+ @keyframes ek-sway { 0%,100% {transform:rotate(-1.5deg)} 50% {transform:rotate(1.5deg)} }
46
+ @keyframes ek-bob { 0%,100% {transform:translateY(1px)} 50% {transform:translateY(-3px)} }
47
+ @keyframes ek-step { 0%,50%,100% {transform:translateY(0)} 25% {transform:translateY(-6px)} }
48
+ @keyframes ek-shadow { 0%,100% {transform:scaleX(1)} 50% {transform:scaleX(.92)} }
49
+ @keyframes ek-recede { 0% {transform:translateY(56px) scaleX(1);opacity:0} 10% {opacity:1} 85% {opacity:1} 100% {transform:translateY(0) scaleX(.4);opacity:0} }
34
50
  @media (prefers-reduced-motion:reduce) { .ek-mascot * {animation:none !important;transition:none !important} }
35
51
 
36
52
  .ek-companion--rail { padding:12px 20px; text-decoration:none; }