@knpkv/alchemy-console 0.2.0 → 0.3.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.
package/README.md CHANGED
@@ -8,8 +8,15 @@ prints the link without opening a browser. CI, non-interactive stdout and
8
8
  `alchemy-console serve` always prints only and runs until SIGINT or SIGTERM.
9
9
  Closing a browser tab leaves the foreground server running.
10
10
 
11
- The current browser page performs login, then navigates to JSON at `/api/stacks`.
12
- Stack screens will follow the approved design references in a separate change.
11
+ The browser shows stacks grouped by app, with desktop columns and compact phone
12
+ rows. Read again starts a fresh discovery request and clears the previous rows.
13
+ Loading, empty, incomplete discovery and owner sign-in failures are explicit.
14
+ Missing metadata reads Unknown; zero resources remains zero. No polling, cached
15
+ rows, plan, apply or destroy runs. Stage names and resource counts are plain text;
16
+ the detail and resources screens are a later change.
17
+
18
+ The standalone wrapper shows Alchemy console, the actual local listener address
19
+ and owner session status. It adds no Relay host inventory or shell navigation.
13
20
 
14
21
  Configuration is read at startup:
15
22
 
@@ -67,6 +74,26 @@ remain in the server and HttpOnly cookie. Every unsafe API method is refused,
67
74
  including apply and destroy. Reads reject foreign origins and cross-site browser
68
75
  requests. Listener and request Host are restricted to the bound loopback address.
69
76
 
77
+ ## Browser views
78
+
79
+ Import `StackListView` from `@knpkv/alchemy-console/views` and its stylesheet from
80
+ `@knpkv/alchemy-console/views/style.css`. Mount it inside a Rly style/theme boundary.
81
+ Views consumers supply the optional React, React DOM and Rly peers; the standalone
82
+ browser shell already bundles them.
83
+ The application supplies `state`, a safe product label and host sign-in instruction,
84
+ and `onReadAgain`. The views fetch nothing, read no
85
+ configuration, and retain no data. `Ready.checkedAt` describes the actual completed
86
+ request. Replace state with `Loading` before reading; failures and unauthorized
87
+ states contain no previous rows. Only normalized authenticated client-visible
88
+ stack metadata belongs here, never raw provider locators or credentials.
89
+
90
+ The standalone client uses the typed API and sends same-origin cookies with
91
+ no-store requests. The fragment is removed from history before its bootstrap
92
+ POST. Tokens never enter storage, logs or telemetry. CSS and font assets are
93
+ served from the same listener with a self-only CSP, without inline styles. The
94
+ decorative empty-state SVG uses the approved Stacks illustration from this MIT
95
+ repository, with presentation attributes rather than inline styles.
96
+
70
97
  ## Data readers
71
98
 
72
99
  The root export provides project discovery, lockfile version lookup, local
@@ -0,0 +1,60 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="88" height="88" aria-hidden="true" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter">
2
+ <defs fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter">
3
+ <radialGradient id="bpkgsrc-88-dc-30" cx="38%" cy="30%" r="80%" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><stop offset="0" stop-color="oklch(0.86 0.12 273)" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /><stop offset="0.55" stop-color="oklch(0.66 0.17 273)" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /><stop offset="1" stop-color="oklch(0.46 0.17 291)" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /></radialGradient>
4
+ <radialGradient id="spkgsrc-88-dc-30" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><stop offset="0" stop-color="black" stop-opacity="0.28" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /><stop offset="1" stop-color="black" stop-opacity="0" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /></radialGradient>
5
+ <radialGradient id="cpkgsrc-88-dc-30" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><stop offset="0" stop-color="oklch(0.95 0.1 264)" stop-opacity="0.95" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /><stop offset="1" stop-color="oklch(0.75 0.15 264)" stop-opacity="0" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /></radialGradient>
6
+ <radialGradient id="gpkgsrc-88-dc-30" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><stop offset="0" stop-color="white" stop-opacity="0.55" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /><stop offset="1" stop-color="white" stop-opacity="0" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /></radialGradient>
7
+ <linearGradient id="dpkgsrc-88-dc-30" x1="0" x2="0" y1="0" y2="1" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><stop offset="0.55" stop-color="oklch(0.3 0.12 291)" stop-opacity="0" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /><stop offset="1" stop-color="oklch(0.3 0.12 291)" stop-opacity="0.45" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /></linearGradient>
8
+ <radialGradient id="wpkgsrc-88-dc-30" cy="40%" r="60%" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><stop offset="0.6" stop-color="oklch(0.99 0.006 264)" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /><stop offset="1" stop-color="oklch(0.86 0.03 264)" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /></radialGradient>
9
+ <radialGradient id="ipkgsrc-88-dc-30" cy="72%" r="70%" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><stop offset="0" stop-color="oklch(0.62 0.15 264)" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /><stop offset="1" stop-color="oklch(0.3 0.09 264)" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /></radialGradient>
10
+ <clipPath id="kpkgsrc-88-dc-30" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><path d="M89.0 58.1C88.9 61.1 88.9 64.0 88.3 66.9C87.8 69.8 87.2 73.0 85.7 75.7C84.2 78.5 82.0 81.2 79.3 83.2C76.6 85.1 73.0 86.4 69.7 87.2C66.3 88.0 62.6 87.9 59.4 88.0C56.1 88.1 53.1 87.7 50.0 87.7C46.9 87.6 44.0 87.8 40.7 87.6C37.5 87.5 33.9 87.6 30.6 86.9C27.2 86.1 23.5 85.0 20.7 83.2C17.9 81.3 15.4 78.6 13.8 76.0C12.3 73.3 11.7 70.0 11.2 67.0C10.7 64.0 10.9 61.1 11.0 58.1C11.1 55.2 11.1 52.3 11.7 49.4C12.2 46.4 12.8 43.2 14.3 40.5C15.8 37.8 18.0 35.0 20.7 33.1C23.4 31.2 27.0 29.9 30.3 29.1C33.7 28.3 37.4 28.3 40.6 28.3C43.9 28.2 46.9 28.5 50.0 28.6C53.1 28.7 56.0 28.5 59.3 28.6C62.5 28.8 66.1 28.7 69.4 29.4C72.8 30.2 76.5 31.3 79.3 33.1C82.1 34.9 84.6 37.6 86.2 40.3C87.7 43.0 88.3 46.3 88.8 49.3C89.3 52.2 89.1 55.2 89.0 58.1Z" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /></clipPath>
11
+ <clipPath id="e0pkgsrc-88-dc-30" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><ellipse cx="39.07" cy="52.158168587244646" rx="5.4" ry="5.832000000000001" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /></clipPath>
12
+ <clipPath id="o0pkgsrc-88-dc-30" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><ellipse cx="39.07" cy="52.158168587244646" rx="6.6000000000000005" ry="7.032000000000001" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /></clipPath>
13
+ <clipPath id="u0pkgsrc-88-dc-30" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><path d="M31.47 64.35V42.78Q39.07 46.28 46.67 42.78V64.35Z" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" transform="translate(0 2.052)" /></clipPath>
14
+ <clipPath id="l0pkgsrc-88-dc-30" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><path d="M31.47 39.96V58.04Q39.07 61.54 46.67 58.04V39.96Z" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /></clipPath><clipPath id="e1pkgsrc-88-dc-30" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><ellipse cx="60.93" cy="52.158168587244646" rx="5.4" ry="5.832000000000001" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /></clipPath>
15
+ <clipPath id="o1pkgsrc-88-dc-30" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><ellipse cx="60.93" cy="52.158168587244646" rx="6.6000000000000005" ry="7.032000000000001" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /></clipPath>
16
+ <clipPath id="u1pkgsrc-88-dc-30" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><path d="M53.33 64.35V42.78Q60.93 46.28 68.53 42.78V64.35Z" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" transform="translate(0 2.052)" /></clipPath>
17
+ <clipPath id="l1pkgsrc-88-dc-30" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><path d="M53.33 39.96V58.04Q60.93 61.54 68.53 58.04V39.96Z" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /></clipPath>
18
+ </defs>
19
+ <ellipse cx="50" cy="91" fill="url(#spkgsrc-88-dc-30)" rx="32.76" ry="5" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" />
20
+ <circle cx="50" cy="55" r="46" fill="none" stroke="oklch(0.4901 0.013 274.7)" stroke-width="2.5px" opacity="0" stroke-linecap="butt" stroke-linejoin="miter" />
21
+ <circle cx="50" cy="55" r="46" pathLength="100" fill="none" stroke="oklch(0.4901 0.013 274.7)" stroke-width="1.3px" opacity="0" stroke-linecap="round" stroke-linejoin="miter" stroke-dasharray="9px, 91px" />
22
+ <g transform="translate(76 6) scale(0.8)" display="none" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><circle cx="12" cy="12" r="11" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /><path d="M6 12h12" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" /></g>
23
+ <g fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><g fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><g fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter">
24
+ <path d="M89.0 58.1C88.9 61.1 88.9 64.0 88.3 66.9C87.8 69.8 87.2 73.0 85.7 75.7C84.2 78.5 82.0 81.2 79.3 83.2C76.6 85.1 73.0 86.4 69.7 87.2C66.3 88.0 62.6 87.9 59.4 88.0C56.1 88.1 53.1 87.7 50.0 87.7C46.9 87.6 44.0 87.8 40.7 87.6C37.5 87.5 33.9 87.6 30.6 86.9C27.2 86.1 23.5 85.0 20.7 83.2C17.9 81.3 15.4 78.6 13.8 76.0C12.3 73.3 11.7 70.0 11.2 67.0C10.7 64.0 10.9 61.1 11.0 58.1C11.1 55.2 11.1 52.3 11.7 49.4C12.2 46.4 12.8 43.2 14.3 40.5C15.8 37.8 18.0 35.0 20.7 33.1C23.4 31.2 27.0 29.9 30.3 29.1C33.7 28.3 37.4 28.3 40.6 28.3C43.9 28.2 46.9 28.5 50.0 28.6C53.1 28.7 56.0 28.5 59.3 28.6C62.5 28.8 66.1 28.7 69.4 29.4C72.8 30.2 76.5 31.3 79.3 33.1C82.1 34.9 84.6 37.6 86.2 40.3C87.7 43.0 88.3 46.3 88.8 49.3C89.3 52.2 89.1 55.2 89.0 58.1Z" fill="url(#bpkgsrc-88-dc-30)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" />
25
+ <g clip-path="url(#kpkgsrc-88-dc-30)" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter">
26
+ <circle cx="50" cy="71.27" r="16" fill="url(#cpkgsrc-88-dc-30)" stroke="none" stroke-width="1px" opacity="0.35" stroke-linecap="butt" stroke-linejoin="miter" />
27
+ <rect x="11.00" y="28.26" width="78.00" height="59.74" fill="url(#dpkgsrc-88-dc-30)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" />
28
+ <ellipse cx="34.40" cy="40.21" rx="13.26" ry="5.97" fill="url(#gpkgsrc-88-dc-30)" transform="rotate(-24 34.40 40.21)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" />
29
+ </g>
30
+ </g></g>
31
+ <g fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><g fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter">
32
+ <g fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter">
33
+ <g clip-path="url(#u0pkgsrc-88-dc-30)" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><g clip-path="url(#l0pkgsrc-88-dc-30)" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter">
34
+ <ellipse cx="39.07" cy="52.158168587244646" rx="6.6000000000000005" ry="7.032000000000001" fill="oklch(0.25 0.05 260 / 0.14)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" />
35
+ <ellipse cx="39.07" cy="52.158168587244646" rx="5.4" ry="5.832000000000001" fill="url(#wpkgsrc-88-dc-30)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" />
36
+ <g clip-path="url(#e0pkgsrc-88-dc-30)" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><g fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter">
37
+ <circle cx="39.07" cy="52.158168587244646" r="3.56" fill="url(#ipkgsrc-88-dc-30)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" />
38
+ <circle cx="39.07" cy="52.158168587244646" r="1.85" fill="oklch(0.14 0.02 260)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" />
39
+ <circle cx="37.79" cy="50.73" r="0.93" fill="oklch(1 0 none / 0.92)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" />
40
+ <circle cx="40.28" cy="53.44" r="0.43" fill="oklch(1 0 none / 0.92)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" />
41
+ </g></g>
42
+ </g></g>
43
+ <g clip-path="url(#o0pkgsrc-88-dc-30)" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><g fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" transform="translate(0 2.052)"><path d="M31.47 42.78Q39.07 46.28 46.67 42.78" fill="none" stroke="oklch(0.22 0.04 260 / 0.85)" stroke-width="0.9px" opacity="0" stroke-linecap="round" stroke-linejoin="miter" /></g></g>
44
+ </g><g fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter">
45
+ <g clip-path="url(#u1pkgsrc-88-dc-30)" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><g clip-path="url(#l1pkgsrc-88-dc-30)" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter">
46
+ <ellipse cx="60.93" cy="52.158168587244646" rx="6.6000000000000005" ry="7.032000000000001" fill="oklch(0.25 0.05 260 / 0.14)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" />
47
+ <ellipse cx="60.93" cy="52.158168587244646" rx="5.4" ry="5.832000000000001" fill="url(#wpkgsrc-88-dc-30)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" />
48
+ <g clip-path="url(#e1pkgsrc-88-dc-30)" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><g fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter">
49
+ <circle cx="60.93" cy="52.158168587244646" r="3.56" fill="url(#ipkgsrc-88-dc-30)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" />
50
+ <circle cx="60.93" cy="52.158168587244646" r="1.85" fill="oklch(0.14 0.02 260)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" />
51
+ <circle cx="59.65" cy="50.73" r="0.93" fill="oklch(1 0 none / 0.92)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" />
52
+ <circle cx="62.14" cy="53.44" r="0.43" fill="oklch(1 0 none / 0.92)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" />
53
+ </g></g>
54
+ </g></g>
55
+ <g clip-path="url(#o1pkgsrc-88-dc-30)" fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter"><g fill="rgb(0, 0, 0)" stroke="none" stroke-width="1px" opacity="1" stroke-linecap="butt" stroke-linejoin="miter" transform="translate(0 2.052)"><path d="M53.33 42.78Q60.93 46.28 68.53 42.78" fill="none" stroke="oklch(0.22 0.04 260 / 0.85)" stroke-width="0.9px" opacity="0" stroke-linecap="round" stroke-linejoin="miter" /></g></g>
56
+ </g>
57
+ <path d="M33.67 42.88Q39.07 39.13 44.47 42.23Q39.07 41.73 33.67 42.88Z" fill="oklch(0.36 0.09 291)" stroke="oklch(0.36 0.09 291)" stroke-width="0.6px" opacity="1" stroke-linecap="butt" stroke-linejoin="round" /><path d="M66.33 42.88Q60.93 39.13 55.53 42.23Q60.93 41.73 66.33 42.88Z" fill="oklch(0.36 0.09 291)" stroke="oklch(0.36 0.09 291)" stroke-width="0.6px" opacity="1" stroke-linecap="butt" stroke-linejoin="round" />
58
+ </g></g>
59
+ </g>
60
+ </svg>