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
@@ -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; }
@@ -1,4 +1,4 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 210 200" width="200" height="200" class="ek-mascot ek-mascot--loading" data-expression="excited" data-variant="loading" role="img"><style>:root {/* ---------- MASCOT IDENTITY ----------
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="-2 4 214 214" width="200" height="200" class="ek-mascot ek-mascot--loading" data-expression="cool" data-variant="loading" role="img"><style>:root {/* ---------- MASCOT IDENTITY ----------
2
2
  Archer Cloak artwork keeps its approved identity across both grounds.
3
3
  UI chrome continues to use the surface, text and accent roles below. */
4
4
  --mascot-coral: #ef9875;
@@ -18,52 +18,75 @@ 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; }
37
53
  .ek-mascot-message > :last-child { min-width:0; overflow-wrap:break-word; }
38
54
  .ek-mascot-status { display:inline-flex; align-items:center; gap:6px; }
39
- </style><title>Eklavya drawing a bow</title><g class="ek-mascot__character">
40
- <ellipse cx="94" cy="185" rx="49" ry="5" fill="var(--mascot-shadow)"/>
41
- <path d="M51 124Q29 137 34 168l23-8 17-27m59-9q26 8 21 39l-24-7-17-22" fill="var(--mascot-cloak-dark)"/>
42
- <path d="M65 157v23q0 8 16 6l4-28m21 0 1 23q2 8 17 4v-29" fill="var(--mascot-coral)"/>
43
- <path d="M58 122q35-16 72 0l8 46q-19 9-42 1-23 7-43-1Z" fill="var(--mascot-cloak)"/>
44
- <path d="M96 127v40m-31-22q29 9 64 0" fill="none" stroke="var(--mascot-cloak-dark)" stroke-width="3"/>
45
- <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"/>
46
- <path d="M57 114l37 10m42-10-39 10" fill="none" stroke="var(--mascot-thread)" stroke-width="2"/>
47
- <path d="M54 148h80v11H54Z" fill="var(--mascot-ink)"/>
48
- <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)"/>
49
- <g class="ek-mascot__head">
55
+ </style><title>Eklavya walking toward you</title><g class="ek-mascot__ground" fill="none" stroke="var(--mascot-metal)" stroke-width="2" stroke-linecap="round" opacity=".5">
56
+ <path d="M-20 218 63 162m147 56-83-56"/>
57
+ <path class="ek-mascot__rung" d="M-10 162h210" transform="matrix(1 0 0 1 0 56)"/><path class="ek-mascot__rung" d="M-10 162h210" transform="matrix(0.75 0 0 1 23.75 34)"/><path class="ek-mascot__rung" d="M-10 162h210" transform="matrix(0.55 0 0 1 42.75 17)"/><path class="ek-mascot__rung" d="M-10 162h210" transform="matrix(0.42 0 0 1 55.1 6)"/>
58
+ </g>
59
+ <ellipse class="ek-mascot__shadow" cx="95" cy="186" rx="40" ry="5" fill="var(--mascot-shadow)"/>
60
+ <g class="ek-mascot__walker">
61
+ <path class="ek-mascot__leg ek-mascot__leg--l" d="M65 157v23q0 8 16 6l4-28Z" fill="var(--mascot-coral)"/>
62
+ <path class="ek-mascot__leg ek-mascot__leg--r" d="M106 158l1 23q2 8 17 4v-29Z" fill="var(--mascot-coral)"/>
63
+ <g class="ek-mascot__torso">
64
+ <path d="M51 124Q29 137 34 168l23-8 17-27m59-9q26 8 21 39l-24-7-17-22" fill="var(--mascot-cloak-dark)"/>
65
+ <path d="M58 122q35-16 72 0l8 46q-19 9-42 1-23 7-43-1Z" fill="var(--mascot-cloak)"/>
66
+ <path d="M96 127v40m-31-22q29 9 64 0" fill="none" stroke="var(--mascot-cloak-dark)" stroke-width="3"/>
67
+ <g class="ek-mascot__hand ek-mascot__hand--arrow" stroke-linecap="round" stroke-linejoin="round">
68
+ <path d="M60 118 47 141" stroke="var(--mascot-cloak)" stroke-width="13"/>
69
+ <path d="M46 104v72" stroke="var(--mascot-wood-dark)" stroke-width="3"/>
70
+ <path d="M41 174l5 11 5-11Z" fill="var(--mascot-thread)" stroke="var(--mascot-wood)" stroke-width="1.5"/>
71
+ <path d="M46 106l-6-9 1 11 5 5 5-5 1-11Z" fill="var(--mascot-thread)"/>
72
+ <rect x="39" y="136" width="15" height="15" rx="7" fill="var(--mascot-coral)"/>
73
+ </g>
74
+ <g class="ek-mascot__hand ek-mascot__hand--bow" fill="none" stroke-linecap="round">
75
+ <path d="M130 118 151 141" stroke="var(--mascot-cloak)" stroke-width="13"/>
76
+ <path d="M152 90v104" stroke="var(--mascot-string)" stroke-width="1.5"/>
77
+ <path d="M152 90q16 52 0 104" stroke="var(--mascot-wood-dark)" stroke-width="7"/>
78
+ <path d="M153 90q13 52 0 104" stroke="var(--mascot-wood)" stroke-width="3.5"/>
79
+ <rect x="151" y="135" width="15" height="15" rx="7" fill="var(--mascot-coral)"/>
80
+ </g>
81
+ <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"/>
82
+ <path d="M57 114l37 10m42-10-39 10" fill="none" stroke="var(--mascot-thread)" stroke-width="2"/>
83
+ <path d="M54 148h80v11H54Z" fill="var(--mascot-ink)"/>
84
+ <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)"/>
85
+ <g class="ek-mascot__head">
50
86
  <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"/>
51
87
  <path d="M48 45h18V32h18V21h19" fill="none" stroke="var(--mascot-highlight)" stroke-width="3" stroke-linejoin="round"/>
52
- <g class="ek-mascot__face" fill="var(--mascot-ink)" stroke="var(--mascot-ink)" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><rect x="64" y="64" width="10" height="19" rx="5"/><rect x="110" y="64" width="10" height="19" rx="5"/><path d="M81 87q11 22 22 0Z" fill="var(--mascot-ink)"/><path d="M87 97q5-3 10 0" stroke="var(--mascot-coral)"/></g>
53
- </g>
54
- <circle cx="96" cy="125" r="7" fill="var(--mascot-metal)"/><circle cx="94" cy="123" r="2" fill="var(--mascot-thread)"/>
55
- <g class="ek-mascot__bow" fill="none" stroke-linecap="round" stroke-linejoin="round">
56
- <path d="M157 78q36 47 0 94" stroke="var(--mascot-wood-dark)" stroke-width="8"/>
57
- <path d="M158 78q32 47 0 94" stroke="var(--mascot-wood)" stroke-width="4"/>
58
- <path class="ek-mascot__string" d="M157 78l-88 50 88 44" stroke="var(--mascot-string)" stroke-width="1.8"/>
88
+ <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
89
  </g>
60
- <path d="M130 127l16-8q10-1 13 5 2 8-6 11l-15 4" fill="var(--mascot-cloak)"/>
61
- <rect x="147" y="117" width="15" height="18" rx="7" fill="var(--mascot-coral)"/>
62
- <g class="ek-mascot__arrow" stroke-linecap="round" stroke-linejoin="round">
63
- <path d="M69 128h114" stroke="var(--mascot-wood-dark)" stroke-width="3"/>
64
- <path d="M179 120l13 8-13 8Z" fill="var(--mascot-thread)" stroke="var(--mascot-wood)" stroke-width="1.5"/>
65
- <path d="M71 128l-9-9 10 1 8 8-8 8-10 1Z" fill="var(--mascot-thread)"/>
90
+ <circle cx="96" cy="125" r="7" fill="var(--mascot-metal)"/><circle cx="94" cy="123" r="2" fill="var(--mascot-thread)"/>
66
91
  </g>
67
- <path d="M58 128q-11-6-14 5-2 9 8 13l17-8" fill="var(--mascot-cloak)"/>
68
- <rect x="62" y="121" width="18" height="16" rx="7" fill="var(--mascot-coral)"/>
69
- </g><circle class="ek-mascot__ripple" cx="196" cy="128" r="10" fill="none" stroke="var(--mascot-thread)" stroke-width="2"/></svg>
92
+ </g></svg>
@@ -3,19 +3,35 @@ eklavya-mascot { display:inline-flex; flex-shrink:0; vertical-align:middle; line
3
3
  .ek-mascot { display:block; overflow:visible; flex-shrink:0; }
4
4
  .ek-mascot[data-variant="face"] { overflow:hidden; }
5
5
  .ek-mascot__face path:not([fill]) { fill:none; }
6
- .ek-mascot--loading .ek-mascot__character { animation:ek-breathe 2.8s ease-in-out infinite; transform-origin:95px 185px; }
7
- .ek-mascot--loading .ek-mascot__arrow { animation:ek-release 2.8s cubic-bezier(.3,0,.3,1) infinite; }
8
- .ek-mascot--loading .ek-mascot__string { animation:ek-draw 2.8s ease-in-out infinite; transform-origin:157px 125px; }
9
- .ek-mascot--loading .ek-mascot__ripple { animation:ek-hit 2.8s ease-out infinite; transform-origin:196px 128px; opacity:0; }
10
- @keyframes ek-breathe { 0%,100% {transform:translateY(0)} 35%,55% {transform:translateY(-3px) rotate(-1deg)} }
11
- @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} }
12
- @keyframes ek-draw { 0%,100% {transform:scaleX(1)} 40%,52% {transform:scaleX(1.11)} 60% {transform:scaleX(.08)} 68% {transform:scaleX(1)} }
13
- @keyframes ek-hit { 0%,60%,100% {opacity:0;transform:scale(.4)} 68% {opacity:.8;transform:scale(.7)} 88% {opacity:0;transform:scale(1.7)} }
14
6
  .ek-companion { display:flex;align-items:center;gap:12px;color:var(--dim);font:13px/1.5 var(--font-body); }
15
7
  .ek-companion--hero { margin-bottom:16px; }
16
8
  .ek-companion--hero b { display:block;color:var(--ink);font-weight:600; }
17
9
  .ek-mascot-message { display:flex;align-items:center;gap:16px;padding:16px 0; }
18
10
  .ek-mascot-message p {margin:0}
11
+ /* Secondary loader: draws and releases an arrow over 2.8 s. */
12
+ .ek-mascot--draw .ek-mascot__character { animation:ek-breathe 2.8s ease-in-out infinite; transform-origin:95px 185px; }
13
+ .ek-mascot--draw .ek-mascot__arrow { animation:ek-release 2.8s cubic-bezier(.3,0,.3,1) infinite; }
14
+ .ek-mascot--draw .ek-mascot__string { animation:ek-draw 2.8s ease-in-out infinite; transform-origin:157px 125px; }
15
+ .ek-mascot--draw .ek-mascot__ripple { animation:ek-hit 2.8s ease-out infinite; transform-origin:196px 128px; opacity:0; }
16
+ @keyframes ek-breathe { 0%,100% {transform:translateY(0)} 35%,55% {transform:translateY(-3px) rotate(-1deg)} }
17
+ @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} }
18
+ @keyframes ek-draw { 0%,100% {transform:scaleX(1)} 40%,52% {transform:scaleX(1.11)} 60% {transform:scaleX(.08)} 68% {transform:scaleX(1)} }
19
+ @keyframes ek-hit { 0%,60%,100% {opacity:0;transform:scale(.4)} 68% {opacity:.8;transform:scale(.7)} 88% {opacity:0;transform:scale(1.7)} }
20
+ /* 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. */
21
+ .ek-mascot--loading .ek-mascot__walker { animation:ek-sway 1.2s ease-in-out infinite; transform-origin:95px 185px; }
22
+ .ek-mascot--loading .ek-mascot__torso { animation:ek-bob .6s ease-in-out infinite; }
23
+ .ek-mascot--loading .ek-mascot__leg, .ek-mascot--loading .ek-mascot__hand { animation:ek-step 1.2s ease-in-out infinite; }
24
+ .ek-mascot--loading .ek-mascot__leg--r, .ek-mascot--loading .ek-mascot__hand--arrow { animation-delay:-.6s; }
25
+ .ek-mascot--loading .ek-mascot__shadow { animation:ek-shadow .6s ease-in-out infinite; transform-origin:95px 186px; }
26
+ .ek-mascot--loading .ek-mascot__rung { animation:ek-recede 1.2s cubic-bezier(.15,.6,.4,1) infinite; transform-origin:95px 162px; }
27
+ .ek-mascot--loading .ek-mascot__rung:nth-child(3) { animation-delay:-.3s; }
28
+ .ek-mascot--loading .ek-mascot__rung:nth-child(4) { animation-delay:-.6s; }
29
+ .ek-mascot--loading .ek-mascot__rung:nth-child(5) { animation-delay:-.9s; }
30
+ @keyframes ek-sway { 0%,100% {transform:rotate(-1.5deg)} 50% {transform:rotate(1.5deg)} }
31
+ @keyframes ek-bob { 0%,100% {transform:translateY(1px)} 50% {transform:translateY(-3px)} }
32
+ @keyframes ek-step { 0%,50%,100% {transform:translateY(0)} 25% {transform:translateY(-6px)} }
33
+ @keyframes ek-shadow { 0%,100% {transform:scaleX(1)} 50% {transform:scaleX(.92)} }
34
+ @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} }
19
35
  @media (prefers-reduced-motion:reduce) { .ek-mascot * {animation:none !important;transition:none !important} }
20
36
 
21
37
  .ek-companion--rail { padding:12px 20px; text-decoration:none; }
@@ -4,10 +4,13 @@
4
4
  const expressions = Object.freeze({
5
5
  neutral: 'Calm and attentive', happy: 'Happy', excited: 'Excited', celebrate: 'Celebrating',
6
6
  curious: 'Curious', thinking: 'Thinking', focused: 'Focused', wink: 'Offering a tip',
7
- surprised: 'Surprised', confused: 'Seeking clarity', worried: 'Concerned', sad: 'Sad', sleepy: 'Resting'
7
+ surprised: 'Surprised', confused: 'Seeking clarity', worried: 'Concerned', sad: 'Sad', sleepy: 'Resting',
8
+ proud: 'Proud', determined: 'Determined', idea: 'Having an idea', laugh: 'Laughing', love: 'Loving it',
9
+ shy: 'Shy', relieved: 'Relieved', starstruck: 'Starstruck', cool: 'Cool and confident', hopeful: 'Hopeful',
10
+ nervous: 'Nervous'
8
11
  });
9
12
  const states = Object.freeze({ idle: 'sleepy', info: 'neutral', success: 'happy', complete: 'celebrate',
10
- milestone: 'celebrate', empty: 'curious', notFound: 'curious', loading: 'excited',
13
+ milestone: 'celebrate', empty: 'curious', notFound: 'curious', loading: 'cool',
11
14
  review: 'focused', tip: 'wink', new: 'surprised', validation: 'confused', warning: 'worried', error: 'sad' });
12
15
  const own = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
13
16
  const escape = (text) => String(text).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
@@ -17,9 +20,14 @@
17
20
  const eyes = '<rect x="64" y="64" width="10" height="19" rx="5"/><rect x="110" y="64" width="10" height="19" rx="5"/>';
18
21
  const smile = '<path d="M83 88q9 10 18 0"/>';
19
22
  const closed = '<path d="M64 74q5-7 10 0m36 0q5-7 10 0"/>';
23
+ const shine = '<circle cx="71" cy="69" r="2" fill="var(--mascot-highlight)" stroke="none"/><circle cx="117" cy="69" r="2" fill="var(--mascot-highlight)" stroke="none"/>';
24
+ const blush = '<g fill="var(--mascot-coral-dark)" stroke="none" opacity=".55"><ellipse cx="60" cy="91" rx="7" ry="4"/><ellipse cx="124" cy="91" rx="7" ry="4"/></g>';
25
+ const sweat = '<path d="M134 50q-5 7-5 10a5 5 0 0 0 10 0q0-3-5-10Z" fill="var(--mascot-thread)" stroke-width="1.5"/>';
26
+ const open = '<path d="M80 86q12 24 24 0Z" fill="var(--mascot-ink)"/><path d="M86 97q6-4 12 0" stroke="var(--mascot-coral)"/>';
20
27
  const brows = { focused: 'M62 57l14 4m32 0 14-4', thinking: 'M62 58h14m33-2 13 3',
21
28
  worried:'M62 59l14-6m32 0 14 6', sad:'M62 60l14-5m32 0 14 5',
22
- confused:'M62 58l14-5m32 4h14', curious:'M62 59h14m32-6 14 5' };
29
+ confused:'M62 58l14-5m32 4h14', curious:'M62 59h14m32-6 14 5', determined:'M61 56l15 7m32 0 15-7',
30
+ proud:'M62 56q7-4 14 0m32 0q7-4 14 0', hopeful:'M62 54l14-3m32 0 14 3', nervous:'M62 58l14-4m32 0 14 4' };
23
31
  const faces = {
24
32
  neutral: eyes + '<path d="M84 89q8 6 16 0"/>', happy: closed + smile,
25
33
  celebrate: closed + '<path d="M82 87q10 22 20 0Z" fill="var(--mascot-ink)"/><path d="M87 98h10" stroke="var(--mascot-coral)"/>',
@@ -28,13 +36,23 @@
28
36
  focused: eyes + '<path d="M85 91h14"/>', wink: '<rect x="64" y="64" width="10" height="19" rx="5"/><path d="M110 74q5-7 10 0"/>' + smile,
29
37
  surprised: eyes + '<ellipse cx="92" cy="92" rx="5" ry="7" fill="var(--mascot-ink)"/>',
30
38
  confused: eyes + '<path d="M83 93q5-7 10-2t9-2"/>', worried: eyes + '<path d="M83 95q9-9 18 0"/>',
31
- sad: eyes + '<path d="M83 96q9-9 18 0"/>', sleepy: '<path d="M64 75q5 5 10 0m36 0q5 5 10 0m-30 16h10"/>'
39
+ sad: eyes + '<path d="M83 96q9-9 18 0"/>', sleepy: '<path d="M64 75q5 5 10 0m36 0q5 5 10 0m-30 16h10"/>',
40
+ proud: closed + '<path d="M80 87q12 11 24 0"/>', determined: eyes + '<path d="M84 92q8-3 16 0"/>',
41
+ idea: eyes + shine + '<path d="M81 87q11 18 22 0Z" fill="var(--mascot-ink)"/>',
42
+ laugh: '<path d="M64 67l10 7-10 7m56-14-10 7 10 7"/>' + open + blush,
43
+ love: '<path d="M69 82l-7-7a4 4 0 0 1 7-5 4 4 0 0 1 7 5Z m46 0l-7-7a4 4 0 0 1 7-5 4 4 0 0 1 7 5Z" fill="var(--mascot-ink)"/><path d="M84 89q4 4 8 0q4 4 8 0"/>' + blush,
44
+ shy: '<rect x="62" y="68" width="10" height="17" rx="5"/><rect x="108" y="68" width="10" height="17" rx="5"/><path d="M86 93q3-2 6 0t6 0"/>' + blush,
45
+ relieved: closed + smile + sweat,
46
+ starstruck: '<path d="M69 63l3 8 8 3-8 3-3 8-3-8-8-3 8-3Z m46 0l3 8 8 3-8 3-3 8-3-8-8-3 8-3Z" fill="var(--mascot-ink)"/>' + open,
47
+ cool: '<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"/>',
48
+ hopeful: '<rect x="64" y="60" width="10" height="19" rx="5"/><rect x="110" y="60" width="10" height="19" rx="5"/>' + shine.replaceAll('cy="69"', 'cy="65"') + '<path d="M86 89q6 5 12 0"/>',
49
+ nervous: '<rect x="68" y="64" width="10" height="19" rx="5"/><rect x="114" y="64" width="10" height="19" rx="5"/><path d="M82 93q3-3 6 0t6 0t6 0t6 0"/>' + sweat
32
50
  };
33
51
  return `<g class="ek-mascot__face" fill="var(--mascot-ink)" stroke="var(--mascot-ink)" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round">${faces[expression]}${brows[expression] ? `<path d="${brows[expression]}" fill="none"/>` : ''}</g>`;
34
52
  }
35
53
  function svg({ expression = 'neutral', state, variant = 'body', size = 64, label = null } = {}) {
36
54
  expression = state ? expressionForState(state) : own(expressions, expression) ? expression : 'neutral';
37
- variant = ['body','face','loading'].includes(variant) ? variant : 'body';
55
+ variant = ['body','face','loading','draw'].includes(variant) ? variant : 'body';
38
56
  const loading = variant === 'loading';
39
57
  const px = dimension(size);
40
58
  const head = `<g class="ek-mascot__head">
@@ -69,8 +87,47 @@
69
87
  <path d="M58 128q-11-6-14 5-2 9 8 13l17-8" fill="var(--mascot-cloak)"/>
70
88
  <rect x="62" y="121" width="18" height="16" rx="7" fill="var(--mascot-coral)"/>
71
89
  </g>`;
72
- const accents = expression === 'celebrate' ? '<g fill="var(--mascot-thread)"><path d="m20 47 3-7 3 7 7 3-7 3-3 7-3-7-7-3Z"/><path d="m166 31 2-5 2 5 5 2-5 2-2 5-2-5-5-2Z"/></g>' : '';
73
- return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${variant === 'face' ? '30 12 126 128' : '0 0 210 200'}" width="${px}" height="${px}" class="ek-mascot${loading ? ' ek-mascot--loading' : ''}" data-expression="${expression}" data-variant="${variant}" ${label == null ? 'aria-hidden="true"' : 'role="img"'}>${label == null ? '' : `<title>${escape(label)}</title>`}${accents}${variant === 'face' ? `<path d="M48 109q44-19 88 0l-8 30-33-15-31 15Z" fill="var(--mascot-cloak)"/>${head}<circle cx="95" cy="126" r="6" fill="var(--mascot-metal)"/>` : body}${loading ? '<circle class="ek-mascot__ripple" cx="196" cy="128" r="10" fill="none" stroke="var(--mascot-thread)" stroke-width="2"/>' : ''}</svg>`;
90
+ // The loader faces the viewer mid-stride: arrow held point-down in one hand, bow in the other, ground sliding away.
91
+ const walk = `<g class="ek-mascot__ground" fill="none" stroke="var(--mascot-metal)" stroke-width="2" stroke-linecap="round" opacity=".5">
92
+ <path d="M-20 218 63 162m147 56-83-56"/>
93
+ ${[[56,1],[34,.75],[17,.55],[6,.42]].map(([dy, sx]) => `<path class="ek-mascot__rung" d="M-10 162h210" transform="matrix(${sx} 0 0 1 ${+(95 * (1 - sx)).toFixed(2)} ${dy})"/>`).join('')}
94
+ </g>
95
+ <ellipse class="ek-mascot__shadow" cx="95" cy="186" rx="40" ry="5" fill="var(--mascot-shadow)"/>
96
+ <g class="ek-mascot__walker">
97
+ <path class="ek-mascot__leg ek-mascot__leg--l" d="M65 157v23q0 8 16 6l4-28Z" fill="var(--mascot-coral)"/>
98
+ <path class="ek-mascot__leg ek-mascot__leg--r" d="M106 158l1 23q2 8 17 4v-29Z" fill="var(--mascot-coral)"/>
99
+ <g class="ek-mascot__torso">
100
+ <path d="M51 124Q29 137 34 168l23-8 17-27m59-9q26 8 21 39l-24-7-17-22" fill="var(--mascot-cloak-dark)"/>
101
+ <path d="M58 122q35-16 72 0l8 46q-19 9-42 1-23 7-43-1Z" fill="var(--mascot-cloak)"/>
102
+ <path d="M96 127v40m-31-22q29 9 64 0" fill="none" stroke="var(--mascot-cloak-dark)" stroke-width="3"/>
103
+ <g class="ek-mascot__hand ek-mascot__hand--arrow" stroke-linecap="round" stroke-linejoin="round">
104
+ <path d="M60 118 47 141" stroke="var(--mascot-cloak)" stroke-width="13"/>
105
+ <path d="M46 104v72" stroke="var(--mascot-wood-dark)" stroke-width="3"/>
106
+ <path d="M41 174l5 11 5-11Z" fill="var(--mascot-thread)" stroke="var(--mascot-wood)" stroke-width="1.5"/>
107
+ <path d="M46 106l-6-9 1 11 5 5 5-5 1-11Z" fill="var(--mascot-thread)"/>
108
+ <rect x="39" y="136" width="15" height="15" rx="7" fill="var(--mascot-coral)"/>
109
+ </g>
110
+ <g class="ek-mascot__hand ek-mascot__hand--bow" fill="none" stroke-linecap="round">
111
+ <path d="M130 118 151 141" stroke="var(--mascot-cloak)" stroke-width="13"/>
112
+ <path d="M152 90v104" stroke="var(--mascot-string)" stroke-width="1.5"/>
113
+ <path d="M152 90q16 52 0 104" stroke="var(--mascot-wood-dark)" stroke-width="7"/>
114
+ <path d="M153 90q13 52 0 104" stroke="var(--mascot-wood)" stroke-width="3.5"/>
115
+ <rect x="151" y="135" width="15" height="15" rx="7" fill="var(--mascot-coral)"/>
116
+ </g>
117
+ <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"/>
118
+ <path d="M57 114l37 10m42-10-39 10" fill="none" stroke="var(--mascot-thread)" stroke-width="2"/>
119
+ <path d="M54 148h80v11H54Z" fill="var(--mascot-ink)"/>
120
+ <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)"/>
121
+ ${head}
122
+ <circle cx="96" cy="125" r="7" fill="var(--mascot-metal)"/><circle cx="94" cy="123" r="2" fill="var(--mascot-thread)"/>
123
+ </g>
124
+ </g>`;
125
+ const accents = {
126
+ celebrate: '<g fill="var(--mascot-thread)"><path d="m20 47 3-7 3 7 7 3-7 3-3 7-3-7-7-3Z"/><path d="m166 31 2-5 2 5 5 2-5 2-2 5-2-5-5-2Z"/></g>',
127
+ proud: '<path d="m144 26 2-5 2 5 5 2-5 2-2 5-2-5-5-2Z" fill="var(--mascot-thread)"/>',
128
+ idea: '<g stroke-linecap="round"><path d="M142 18v-3m11 7 2-2m-24 2-2-2" stroke="var(--mascot-thread)" stroke-width="2"/><circle cx="142" cy="31" r="8" fill="var(--mascot-thread)" stroke="var(--mascot-ink)" stroke-width="2"/><path d="M139 40h6v4h-6Z" fill="var(--mascot-metal)"/></g>'
129
+ }[expression] || '';
130
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${variant === 'face' ? '30 12 126 128' : loading ? '-2 4 214 214' : '0 0 210 200'}" width="${px}" height="${px}" class="ek-mascot${loading ? ' ek-mascot--loading' : ''}${variant === 'draw' ? ' ek-mascot--draw' : ''}" data-expression="${expression}" data-variant="${variant}" ${label == null ? 'aria-hidden="true"' : 'role="img"'}>${label == null ? '' : `<title>${escape(label)}</title>`}${accents}${variant === 'face' ? `<path d="M48 109q44-19 88 0l-8 30-33-15-31 15Z" fill="var(--mascot-cloak)"/>${head}<circle cx="95" cy="126" r="6" fill="var(--mascot-metal)"/>` : loading ? walk : body}${variant === 'draw' ? '<circle class="ek-mascot__ripple" cx="196" cy="128" r="10" fill="none" stroke="var(--mascot-thread)" stroke-width="2"/>' : ''}</svg>`;
74
131
  }
75
132
  root.EklavyaMascot = Object.freeze({ expressions, states, expressionForState, svg });
76
133
  if (root.customElements && !root.customElements.get('eklavya-mascot')) {
@@ -3,7 +3,7 @@
3
3
  <head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
4
4
  <title>Eklavya — Mascot studio</title><meta name="robots" content="noindex, follow"><meta name="description" content="The Eklavya archer companion: expressions, motion, and reusable design-system components.">
5
5
  <script>try{document.documentElement.dataset.mode=localStorage.getItem('eklavya-ground')==='paper'?'paper':'ink'}catch(e){}</script>
6
- <link rel="stylesheet" href="/tokens.css?v=3"><link rel="stylesheet" href="/brand/mascot/mascot.css?v=1"><script src="/brand/mascot/mascot.js?v=1" defer></script>
6
+ <link rel="stylesheet" href="/tokens.css?v=3"><link rel="stylesheet" href="/brand/mascot/mascot.css?v=4"><script src="/brand/mascot/mascot.js?v=4" defer></script>
7
7
  <style>
8
8
  *{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.6 var(--font-body)}
9
9
  a{color:var(--spot)}button{font:inherit;color:var(--ink);cursor:pointer;background:var(--panel);border:1px solid var(--line-2);border-radius:0;padding:9px 15px}button:hover,button[aria-pressed=true]{border-color:var(--spot);background:var(--spot-soft)}:focus-visible{outline:2px solid var(--spot);outline-offset:4px}
@@ -11,30 +11,34 @@ a{color:var(--spot)}button{font:inherit;color:var(--ink);cursor:pointer;backgrou
11
11
  @media(max-width:760px){.hero{grid-template-columns:1fr;margin:40px 0;gap:0}.stage{min-height:280px}.grid{grid-template-columns:repeat(3,minmax(0,1fr))}.panels{grid-template-columns:1fr}.section-head{display:block}}
12
12
  </style></head><body><main class="studio">
13
13
  <nav class="top" aria-label="Studio navigation"><a class="wordmark" href="/">Eklavya <span class="eyebrow">/ mascot studio</span></a><button id="ground" aria-label="Switch to paper theme">Paper ground</button></nav>
14
- <section class="hero"><div class="intro"><span class="eyebrow">Meet your learning companion</span><h1>A steady aim.<br>A curious mind.</h1><p>The Archer Cloak companion. A warm coral character, a verdigris cloak, and a little encouragement as you learn.</p><p class="notice" id="selected" aria-live="polite">Calm and attentive</p><div class="controls"><button id="pose" aria-pressed="false">Show face only</button><button id="motion" aria-pressed="false">Play loading loop</button></div></div><div class="stage"><eklavya-mascot id="preview" expression="neutral" size="280" label="Eklavya, calm and attentive"></eklavya-mascot></div></section>
14
+ <section class="hero"><div class="intro"><span class="eyebrow">Meet your learning companion</span><h1>A steady aim.<br>A curious mind.</h1><p>The Archer Cloak companion. A warm coral character, a verdigris cloak, and a little encouragement as you learn.</p><p class="notice" id="selected" aria-live="polite">Calm and attentive</p><div class="controls"><button id="pose" aria-pressed="false">Show face only</button><button id="motion" aria-pressed="false">Play loading loop</button><button id="draw" aria-pressed="false">Play bow-draw loop</button></div></div><div class="stage"><eklavya-mascot id="preview" expression="neutral" size="280" label="Eklavya, calm and attentive"></eklavya-mascot></div></section>
15
15
  <section class="section"><div class="section-head"><h2>A face for the moment</h2><p>Choose an expression to preview it.</p></div><div class="grid" id="expressions" role="group" aria-label="Mascot expressions"></div></section>
16
- <section class="section"><h2>Practice in motion</h2><p>Draw, release, reset. A quiet, repeating rhythm for real loading states.</p><div class="panels"><div class="panel"><div class="motion" role="status"><eklavya-mascot state="loading" variant="loading" size="112"></eklavya-mascot><div><strong>Loading your history…</strong><p class="caption">Page loading · 112 px</p></div></div></div><div class="panel"><div class="motion" role="status"><eklavya-mascot state="loading" variant="loading" size="56"></eklavya-mascot><div><strong>Finding the next step…</strong><p class="caption">Inline loading · 56 px</p></div></div></div></div><button id="pause" aria-pressed="false">Pause animations</button><p class="caption">Reduced-motion preferences are respected automatically. Status text remains readable when motion is off.</p></section>
16
+ <section class="section"><h2>Practice in motion</h2><p>Bow in one hand, arrow in the other, walking toward you: the default loader. The bow-draw loop (<code>variant="draw"</code>) is a secondary loader for places that suit it.</p><div class="panels"><div class="panel"><div class="motion" role="status"><eklavya-mascot state="loading" variant="loading" size="112"></eklavya-mascot><div><strong data-verb>Pondering…</strong><p class="caption">Page loading · 112 px</p></div></div></div><div class="panel"><div class="motion" role="status"><eklavya-mascot state="loading" variant="loading" size="56"></eklavya-mascot><div><strong data-verb>Moseying…</strong><p class="caption">Inline loading · 56 px</p></div></div></div></div><button id="pause" aria-pressed="false">Pause animations</button><p class="caption">Reduced-motion preferences are respected automatically. Status text remains readable when motion is off.</p></section>
17
17
  <section class="section"><h2>Small enough to belong</h2><p>Use the face at compact sizes and the full archer when there is room to breathe.</p><div class="sizes" id="sizes"></div></section>
18
18
  <section class="section"><h2>React to the state</h2><p>Choose a UI state. One shared mapping keeps the companion consistent everywhere.</p><div class="state-list" id="states"></div><div class="ek-mascot-message" style="margin-top:16px"><eklavya-mascot id="state-preview" state="empty" size="80"></eklavya-mascot><div><strong id="state-name">empty → curious</strong><p id="state-copy">Nothing here yet. Your next session is a good place to start.</p></div></div></section>
19
19
  <section class="section"><h2>Made to be reused</h2><p>Same artwork on the website and the local dashboard. Use a state for product moments, or an expression for editorial illustrations.</p><pre>&lt;link rel="stylesheet" href="/tokens.css?v=3"&gt;
20
- &lt;link rel="stylesheet" href="/brand/mascot/mascot.css?v=1"&gt;
21
- &lt;script src="/brand/mascot/mascot.js?v=1" defer&gt;&lt;/script&gt;
20
+ &lt;link rel="stylesheet" href="/brand/mascot/mascot.css?v=4"&gt;
21
+ &lt;script src="/brand/mascot/mascot.js?v=4" defer&gt;&lt;/script&gt;
22
22
 
23
23
  &lt;eklavya-mascot state="empty" size="80"&gt;&lt;/eklavya-mascot&gt;
24
24
  &lt;eklavya-mascot expression="wink" variant="face" size="40"&gt;&lt;/eklavya-mascot&gt;
25
25
 
26
26
  &lt;div role="status"&gt;
27
27
  &lt;eklavya-mascot state="loading" variant="loading" size="56"&gt;&lt;/eklavya-mascot&gt;
28
- Loading your history…
28
+ &lt;!-- a random verb for each wait, such as Pondering… --&gt;
29
+ &lt;span&gt;Pondering…&lt;/span&gt;
29
30
  &lt;/div&gt;</pre><p class="caption">Decorative by default. Add a label when the artwork communicates something on its own. Keep errors and guidance in visible text.</p></section>
30
31
  </main><script>
31
32
  window.addEventListener('DOMContentLoaded',()=>{
32
- const M=EklavyaMascot, $=id=>document.getElementById(id), preview=$('preview');let expression='neutral',face=false,loading=false;
33
- function update(){preview.removeAttribute('state');preview.setAttribute('expression',expression);preview.setAttribute('variant',loading?'loading':face?'face':'body');preview.setAttribute('label','Eklavya, '+M.expressions[expression].toLowerCase());$('selected').textContent=M.expressions[expression];$('pose').setAttribute('aria-pressed',String(face));$('motion').setAttribute('aria-pressed',String(loading));document.querySelectorAll('.expression').forEach(b=>b.setAttribute('aria-pressed',String(b.dataset.expression===expression)))}
34
- for(const [key,label] of Object.entries(M.expressions)){const b=document.createElement('button');b.className='expression';b.dataset.expression=key;b.setAttribute('aria-pressed',String(key==='neutral'));b.innerHTML=M.svg({expression:key,variant:'face',size:72})+'<span>'+label+'</span><small>'+key+'</small>';b.addEventListener('click',()=>{expression=key;loading=false;update()});$('expressions').append(b)}
35
- $('pose').onclick=()=>{face=!face;loading=false;update()};$('motion').onclick=()=>{loading=!loading;if(loading)expression='excited';update()};
33
+ const M=EklavyaMascot, $=id=>document.getElementById(id), preview=$('preview');let expression='neutral',face=false,loading=false,drawing=false;
34
+ function update(){preview.removeAttribute('state');preview.setAttribute('expression',expression);preview.setAttribute('variant',loading?'loading':drawing?'draw':face?'face':'body');preview.setAttribute('label','Eklavya, '+M.expressions[expression].toLowerCase());$('selected').textContent=M.expressions[expression];$('pose').setAttribute('aria-pressed',String(face));$('motion').setAttribute('aria-pressed',String(loading));$('draw').setAttribute('aria-pressed',String(drawing));document.querySelectorAll('.expression').forEach(b=>b.setAttribute('aria-pressed',String(b.dataset.expression===expression)))}
35
+ for(const [key,label] of Object.entries(M.expressions)){const b=document.createElement('button');b.className='expression';b.dataset.expression=key;b.setAttribute('aria-pressed',String(key==='neutral'));b.innerHTML=M.svg({expression:key,variant:'face',size:72})+'<span>'+label+'</span><small>'+key+'</small>';b.addEventListener('click',()=>{expression=key;update()});$('expressions').append(b)}
36
+ $('pose').onclick=()=>{face=!face;loading=drawing=false;update()};$('motion').onclick=()=>{loading=!loading;drawing=false;if(loading)expression='cool';update()};$('draw').onclick=()=>{drawing=!drawing;loading=false;if(drawing)expression='excited';update()};
36
37
  $('pause').onclick=()=>{const paused=document.body.classList.toggle('paused');$('pause').setAttribute('aria-pressed',String(paused));$('pause').textContent=paused?'Resume animations':'Pause animations'};
37
- const copy={empty:'Nothing here yet. Your next session is a good place to start.',loading:'Loading your history…',success:'Saved. Keep going.',complete:'Practice complete. Nice work.',error:'This could not load. Try again.',warning:'Review this step before continuing.',validation:'Check the highlighted field.',review:'Take a moment to review.',tip:'A useful detail to consider.',idle:'Ready when you are.'};
38
+ // A sample of the dashboard's LOADER_VERBS: every wait shows a random one, never a fixed word.
39
+ const verbs=['Pondering','Moseying','Brewing','Noodling','Percolating','Wandering'],verb=()=>verbs[Math.floor(Math.random()*verbs.length)]+'…';
40
+ document.querySelectorAll('[data-verb]').forEach(e=>e.textContent=verb());
41
+ const copy={empty:'Nothing here yet. Your next session is a good place to start.',get loading(){return verb()},success:'Saved. Keep going.',complete:'Practice complete. Nice work.',error:'This could not load. Try again.',warning:'Review this step before continuing.',validation:'Check the highlighted field.',review:'Take a moment to review.',tip:'A useful detail to consider.',idle:'Ready when you are.'};
38
42
  for(const [state,mood] of Object.entries(M.states)){const b=document.createElement('button');b.textContent=state;b.setAttribute('aria-pressed',String(state==='empty'));b.onclick=()=>{$('state-preview').setAttribute('state',state);$('state-preview').setAttribute('variant',state==='loading'?'loading':'body');$('state-name').textContent=state+' → '+mood;$('state-copy').textContent=copy[state]||M.expressions[mood];$('states').querySelectorAll('button').forEach(x=>x.setAttribute('aria-pressed',String(x===b)))};$('states').append(b)}
39
43
  for(const size of [24,32,40,64,96,128]){const node=document.createElement('div');node.className='size';node.innerHTML=M.svg({expression:'neutral',variant:size<64?'face':'body',size})+'<span>'+size+' px</span>';$('sizes').append(node)}
40
44
  function ground(){const paper=document.documentElement.dataset.mode==='paper';$('ground').textContent=paper?'Ink ground':'Paper ground';$('ground').setAttribute('aria-label',paper?'Switch to ink theme':'Switch to paper theme')}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eklavya",
3
3
  "displayName": "Eklavya",
4
- "version": "1.86.0",
4
+ "version": "1.87.0",
5
5
  "description": "Learn while your agent works. Turns coding-agent generation time into adaptive, Socratic learning grounded in the code being written.",
6
6
  "author": {
7
7
  "name": "Ajay Kumar"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eklavya",
3
- "version": "1.86.0",
3
+ "version": "1.87.0",
4
4
  "description": "Learn while your agent works — local knowledge graph, spaced repetition, and quiz gating for agent-assisted development.",
5
5
  "license": "MIT",
6
6
  "repository": {