@intentius/behold 0.7.0 → 0.8.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 (76) hide show
  1. package/AGENTS.md +9 -2
  2. package/README.md +12 -2
  3. package/demos.json +8 -0
  4. package/dist/cli.js +750 -132
  5. package/example-argo-estate/README.md +46 -0
  6. package/example-argo-estate/app-a/chant.config.ts +10 -0
  7. package/example-argo-estate/app-a/manifests/app.yaml +54 -0
  8. package/example-argo-estate/app-a/package.json +13 -0
  9. package/example-argo-estate/app-a/src/app.ts +35 -0
  10. package/example-argo-estate/app-a/tsconfig.json +1 -0
  11. package/example-argo-estate/app-b/chant.config.ts +9 -0
  12. package/example-argo-estate/app-b/manifests/app.yaml +54 -0
  13. package/example-argo-estate/app-b/package.json +13 -0
  14. package/example-argo-estate/app-b/src/app.ts +34 -0
  15. package/example-argo-estate/app-b/tsconfig.json +1 -0
  16. package/example-argo-estate/control-plane/chant.config.ts +14 -0
  17. package/example-argo-estate/control-plane/package.json +13 -0
  18. package/example-argo-estate/control-plane/src/argo.ts +69 -0
  19. package/example-argo-estate/control-plane/tsconfig.json +1 -0
  20. package/example-argo-estate/package-lock.json +1949 -0
  21. package/example-argo-estate/package.json +10 -0
  22. package/example-flux-estate/README.md +13 -8
  23. package/example-flux-estate/app-a/package.json +2 -2
  24. package/example-flux-estate/app-b/package.json +2 -2
  25. package/example-flux-estate/control-plane/package.json +2 -2
  26. package/example-flux-estate/control-plane/src/flux.ts +6 -0
  27. package/example-flux-estate/package-lock.json +17 -17
  28. package/example-k8s/README.md +16 -2
  29. package/example-k8s/base/deployment.yaml +21 -0
  30. package/example-k8s/base/kustomization.yaml +3 -0
  31. package/example-k8s/base/service.yaml +12 -0
  32. package/example-k8s/chant.config.ts +10 -0
  33. package/example-k8s/overlays/dev/kustomization.yaml +6 -0
  34. package/example-k8s/package-lock.json +18 -18
  35. package/example-k8s/package.json +3 -3
  36. package/example-writes/package-lock.json +14 -14
  37. package/example-writes/package.json +3 -3
  38. package/package.json +3 -2
  39. package/web/app.js +350 -22
  40. package/web/icons/cncf/argo.svg +1 -0
  41. package/web/icons/cncf/flux.svg +1 -0
  42. package/web/icons/cncf/helm.svg +1 -0
  43. package/web/icons/k8s/c-role.svg +1 -0
  44. package/web/icons/k8s/cm.svg +1 -0
  45. package/web/icons/k8s/crb.svg +1 -0
  46. package/web/icons/k8s/crd.svg +1 -0
  47. package/web/icons/k8s/cronjob.svg +1 -0
  48. package/web/icons/k8s/deploy.svg +1 -0
  49. package/web/icons/k8s/ds.svg +1 -0
  50. package/web/icons/k8s/ep.svg +1 -0
  51. package/web/icons/k8s/group.svg +1 -0
  52. package/web/icons/k8s/hpa.svg +1 -0
  53. package/web/icons/k8s/ing.svg +1 -0
  54. package/web/icons/k8s/job.svg +1 -0
  55. package/web/icons/k8s/limits.svg +1 -0
  56. package/web/icons/k8s/netpol.svg +1 -0
  57. package/web/icons/k8s/ns.svg +1 -0
  58. package/web/icons/k8s/pod.svg +1 -0
  59. package/web/icons/k8s/psp.svg +1 -0
  60. package/web/icons/k8s/pv.svg +1 -0
  61. package/web/icons/k8s/pvc.svg +1 -0
  62. package/web/icons/k8s/quota.svg +1 -0
  63. package/web/icons/k8s/rb.svg +1 -0
  64. package/web/icons/k8s/role.svg +1 -0
  65. package/web/icons/k8s/rs.svg +1 -0
  66. package/web/icons/k8s/sa.svg +1 -0
  67. package/web/icons/k8s/sc.svg +1 -0
  68. package/web/icons/k8s/secret.svg +1 -0
  69. package/web/icons/k8s/sts.svg +1 -0
  70. package/web/icons/k8s/svc.svg +1 -0
  71. package/web/icons/k8s/user.svg +1 -0
  72. package/web/icons/k8s/vol.svg +1 -0
  73. package/web/index.html +192 -84
  74. package/web/layout-store.js +118 -0
  75. package/web/layout-store.test.js +163 -0
  76. package/web/theme.js +58 -6
package/web/app.js CHANGED
@@ -10,6 +10,10 @@
10
10
  // renderPanelView never rewrites, so the select mounts once and survives re-renders).
11
11
  import { initTheme, mountThemePicker, readableOn, colorForCategory, onThemeChange, getTokens } from "./theme.js";
12
12
  import { initPanel, setPanelTab, togglePanelCollapsed, isPanelCollapsed } from "./panel.js";
13
+ // #228: the hand-layout delta store — everything about WHAT gets remembered and
14
+ // under which key. The pointer work and the SVG surgery stay here (see the
15
+ // "Hand layout" section below).
16
+ import { applicable, clearLayout, isEmpty, layoutKey, lensKeyOf, projectKeyOf, readLayout, setDelta, writeLayout } from "./layout-store.js";
13
17
  initTheme();
14
18
  initPanel();
15
19
  mountThemePicker(document.getElementById("panel-theme"));
@@ -63,6 +67,39 @@ function recolorNodesByCategory(ir) {
63
67
  }
64
68
  onThemeChange(() => recolorNodesByCategory());
65
69
 
70
+ // #229, the one motion signature behold spends on data: a node whose drift
71
+ // `_status` CHANGED between two renders pulses once in the colour it just
72
+ // became. Node ids are stable across renders, so the diff is a single map
73
+ // compare — no extra fetch, no per-node bookkeeping. The first render seeds the
74
+ // map and pulses nothing (everything would "change"), and a lens switch that
75
+ // swaps the id set pulses nothing either, since only ids present in BOTH
76
+ // renders can have changed. The CSS keyframes + the reduced-motion guard live
77
+ // in index.html.
78
+ let lastStatusById = new Map();
79
+ function markStatusChanges(ir, statusVar) {
80
+ const prev = lastStatusById;
81
+ const next = new Map();
82
+ for (const n of ir.nodes) {
83
+ const s = n.attrs && n.attrs._status;
84
+ if (s) next.set(n.id, s);
85
+ }
86
+ lastStatusById = next;
87
+ if (!prev.size) return;
88
+ const svg = document.querySelector("#graph svg");
89
+ if (!svg) return;
90
+ for (const g of svg.querySelectorAll("[data-node-id]")) {
91
+ const id = g.getAttribute("data-node-id");
92
+ const now = next.get(id);
93
+ if (!now || prev.get(id) === undefined || prev.get(id) === now) continue;
94
+ // The pulse wears the NEW status's hue, read through whichever vocabulary
95
+ // this graph is painted in — a node going foreign glows yellow in the drift
96
+ // overlay; a component that rolled back glows red in the component view,
97
+ // where the same `warn` means something else entirely.
98
+ g.style.setProperty("--pulse", statusVar[now] || "var(--pending)");
99
+ g.classList.add("status-changed");
100
+ }
101
+ }
102
+
66
103
  // Static-export mode (`behold export`): the SPA runs off a pre-captured bundle
67
104
  // with no backend. Detect the flag the export injects, load its manifest, and
68
105
  // replay every read from `snapshots/` — the graph, zoom dial, radial, inspect,
@@ -141,7 +178,6 @@ function inspect(node) {
141
178
  const section = (title) => {
142
179
  const h = document.createElement("h3");
143
180
  h.textContent = title;
144
- h.style.cssText = "font-size:11px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);margin:14px 0 6px";
145
181
  panel.appendChild(h);
146
182
  const dl = document.createElement("dl");
147
183
  panel.appendChild(dl);
@@ -202,7 +238,6 @@ function inspect(node) {
202
238
  if (isComposite) {
203
239
  const h = document.createElement("h3");
204
240
  h.textContent = `members · ${node.attrs.members}`;
205
- h.style.cssText = "font-size:11px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);margin:14px 0 6px";
206
241
  panel.appendChild(h);
207
242
  const loading = document.createElement("p");
208
243
  loading.style.color = "var(--muted)";
@@ -354,7 +389,7 @@ function inspect(node) {
354
389
  panel.appendChild(p);
355
390
  return;
356
391
  }
357
- renderObserved(panel, j.observed, j.health); // #30 observed state + #26 health
392
+ renderObserved(panel, j.observed, j.health, j.healthDetail); // #30 observed state + #26/#226 health
358
393
  renderDiff(panel, j.diff); // #27 — drift since snapshot
359
394
  renderFieldDrift(panel, j.fieldDrift); // #87 — field-level (per-manager) drift
360
395
  });
@@ -388,11 +423,10 @@ const HEALTH_COLOR = {
388
423
  };
389
424
 
390
425
  // Render a node's observed live state (#30) + health verdict (#26).
391
- function renderObserved(panel, o, health) {
426
+ function renderObserved(panel, o, health, healthDetail) {
392
427
  if (!o) return; // pending/foreign nodes have no observed record in the diff
393
428
  const h = document.createElement("h3");
394
429
  h.textContent = "observed";
395
- h.style.cssText = "font-size:11px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);margin:14px 0 6px";
396
430
  panel.appendChild(h);
397
431
  const dl = document.createElement("dl");
398
432
  const add = (k, v, color) => {
@@ -405,7 +439,14 @@ function renderObserved(panel, o, health) {
405
439
  };
406
440
  // Health first — the "is it well?" verdict, distinct from drift. Absent when
407
441
  // the substrate reports no status (not fabricated).
408
- if (health && health !== "unknown") add("health", health, HEALTH_COLOR[health]);
442
+ // #226: for an Argo Application or a Flux object the verdict comes from the
443
+ // controller's own conditions, and `healthDetail` is the sentence it read
444
+ // there — `Ready=False (BuildFailed)`, `health=Degraded, sync=OutOfSync`.
445
+ // Appended to the verdict rather than given its own row, because it is the
446
+ // same claim said precisely.
447
+ if (health && health !== "unknown") {
448
+ add("health", healthDetail ? `${health} — ${healthDetail}` : health, HEALTH_COLOR[health]);
449
+ }
409
450
  if (o.type) add("type", o.type);
410
451
  if (o.status) add("status", o.status, HEALTH_COLOR[health] || undefined);
411
452
  if (o.physicalId) add("physical id", o.physicalId);
@@ -448,7 +489,6 @@ const DIFF_LABEL = {
448
489
  function renderDiff(panel, diff) {
449
490
  const h = document.createElement("h3");
450
491
  h.textContent = "drift";
451
- h.style.cssText = "font-size:11px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);margin:14px 0 6px";
452
492
  panel.appendChild(h);
453
493
  if (!diff) {
454
494
  const p = document.createElement("p");
@@ -466,7 +506,7 @@ function renderDiff(panel, diff) {
466
506
  // this address makes that visible). Monospace so a request path scans.
467
507
  if (diff.queried) {
468
508
  const q = document.createElement("p");
469
- q.style.cssText = "color:var(--muted);font:11px/1.4 ui-monospace,monospace;overflow-wrap:anywhere;margin-top:2px";
509
+ q.className = "queried"; // mono + muted, styled with the rest of the pane's scale
470
510
  q.textContent = `queried: ${diff.queried} → ${diff.category === "missing" ? "not found" : "read failed"}`;
471
511
  panel.appendChild(q);
472
512
  }
@@ -539,7 +579,6 @@ function renderFieldDrift(panel, fieldDrift) {
539
579
  if (!fieldDrift.drifted.length && !fieldDrift.accepted.length) return; // deep ran, nothing to report for this node
540
580
  const h = document.createElement("h3");
541
581
  h.textContent = "field ownership";
542
- h.style.cssText = "font-size:11px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);margin:14px 0 6px";
543
582
  panel.appendChild(h);
544
583
  const dl = document.createElement("dl");
545
584
  for (const ch of fieldDrift.drifted) {
@@ -734,7 +773,7 @@ function renderPanelView() {
734
773
  lanes.href = "/lanes";
735
774
  lanes.textContent = "lanes →";
736
775
  lanes.title = "The time-lanes view — captured frames of this graph over time";
737
- lanes.style.cssText = "color:var(--pending);text-decoration:none;font-size:12px";
776
+ lanes.style.cssText = "color:var(--pending);text-decoration:none;font-size:var(--t-body)";
738
777
  row.appendChild(lanes);
739
778
  tools.appendChild(row);
740
779
  }
@@ -1210,7 +1249,7 @@ function renderDial() {
1210
1249
  }
1211
1250
  host.style.display = "flex";
1212
1251
  const hint = document.createElement("span");
1213
- hint.style.cssText = "font-size:11px;color:var(--muted);align-self:center";
1252
+ hint.style.cssText = "font-size:var(--t-caption);color:var(--muted);align-self:center";
1214
1253
  hint.textContent = "observe → reconcile → apply needs an environment — pick one in ⌘K (env: …)";
1215
1254
  host.appendChild(hint);
1216
1255
  // A pipeline run (#163) is env-less — its progress still belongs here.
@@ -1283,7 +1322,7 @@ function renderApplyPicker() {
1283
1322
  wrap.style.gap = "6px";
1284
1323
  const sel = document.createElement("select");
1285
1324
  sel.style.cssText =
1286
- "background:var(--panel);color:var(--fg);border:1px solid var(--line);border-radius:6px;padding:3px 8px;font-size:12px";
1325
+ "background:var(--well);color:var(--fg);border:1px solid var(--line);border-radius:var(--r-ctl);padding:3px 8px;font-size:var(--t-body)";
1287
1326
  sel.add(new Option("all components", "all"));
1288
1327
  for (const name of componentChoices) sel.add(new Option(applyOptionLabel(name), name));
1289
1328
  const go = button("Apply →", "", () => {
@@ -1405,7 +1444,7 @@ function renderApplyProgress(state) {
1405
1444
  const wrap = document.createElement("div");
1406
1445
  wrap.style.cssText = "display:flex;flex-direction:column;gap:6px;width:100%;margin-top:4px";
1407
1446
  const summary = document.createElement("div");
1408
- summary.style.cssText = `font-size:11px;color:${APPLY_STATUS_COLOR[state.status] || "var(--muted)"}`;
1447
+ summary.style.cssText = `font-size:var(--t-caption);color:${APPLY_STATUS_COLOR[state.status] || "var(--muted)"}`;
1409
1448
  // A pipeline run (#163) reuses this whole panel — same shape, different
1410
1449
  // executor — and says so instead of claiming to be an apply.
1411
1450
  summary.textContent = `${state.kind || "apply"}: ${state.status}`;
@@ -1414,14 +1453,14 @@ function renderApplyProgress(state) {
1414
1453
  const row = document.createElement("div");
1415
1454
  row.style.cssText = "display:flex;align-items:center;gap:8px;flex-wrap:wrap";
1416
1455
  const label = document.createElement("span");
1417
- label.style.cssText = `font-size:11px;color:${APPLY_STATUS_COLOR[w.status] || "var(--muted)"};min-width:52px`;
1456
+ label.style.cssText = `font-size:var(--t-caption);color:${APPLY_STATUS_COLOR[w.status] || "var(--muted)"};min-width:52px`;
1418
1457
  label.textContent = `wave ${w.wave}`;
1419
1458
  row.appendChild(label);
1420
1459
  for (const cname of w.components) {
1421
1460
  const c = (state.components || []).find((x) => x.component === cname) || { status: "pending" };
1422
1461
  const color = APPLY_STATUS_COLOR[c.status] || "var(--muted)";
1423
1462
  const chip = document.createElement("span");
1424
- chip.style.cssText = `border:1px solid ${color};color:${color};border-radius:6px;padding:2px 8px;font-size:11px`;
1463
+ chip.style.cssText = `border:1px solid ${color};color:${color};border-radius:var(--r-ctl);padding:2px 8px;font-size:var(--t-caption)`;
1425
1464
  const detail = [c.phase, c.step].filter(Boolean).join(" · ");
1426
1465
  chip.textContent = `${cname}${detail ? " · " + detail : ""} (${c.status})`;
1427
1466
  if (c.error) chip.title = c.error;
@@ -1657,6 +1696,7 @@ function render(ir, svg, m) {
1657
1696
  // green — the SVG's own :root shadows behold's override within the graph subtree.
1658
1697
  g.innerHTML = svg.replace(/:root\s*\{[^{}]*--pin-[^{}]*\}/g, "");
1659
1698
  recolorNodesByCategory(ir); // #62: category-hued fills from the theme's full palette
1699
+ markStatusChanges(ir, componentStatus ? COMPONENT_STATUS_VAR : DRIFT_STATUS_VAR); // #229
1660
1700
  const svgEl = g.querySelector("svg");
1661
1701
  if (svgEl) {
1662
1702
  // Drop pinhole's fixed pixel size so the viewBox drives sizing; behold then
@@ -1676,6 +1716,7 @@ function render(ir, svg, m) {
1676
1716
  ensureBackToInfra(g);
1677
1717
  wire(ir);
1678
1718
  if (view.radial && !view.components && !view.logical) addRadialLabels(ir);
1719
+ applyLayout(); // #228: last, so the hand-placed deltas ride on top of every other pass
1679
1720
  renderDial();
1680
1721
  }
1681
1722
 
@@ -1833,6 +1874,11 @@ function ensureZoomControls(host) {
1833
1874
  let py = 0;
1834
1875
  host.addEventListener("mousedown", (e) => {
1835
1876
  if (!vb) return;
1877
+ // #228: pan is the default on empty ground, but a node card (or a box's
1878
+ // title / resize handle) belongs to the layout drag — starting a pan too
1879
+ // would move the graph out from under the thing you grabbed. The layout
1880
+ // drag owns the panMoved latch for the rest of the gesture.
1881
+ if (layoutTargetOf(e)) return;
1836
1882
  drag = true;
1837
1883
  panMoved = false;
1838
1884
  px = e.clientX;
@@ -1862,6 +1908,288 @@ function ensureZoomControls(host) {
1862
1908
  });
1863
1909
  }
1864
1910
 
1911
+ // --- Hand layout (#228): drag a node, resize a containment box -------------
1912
+ // dagre is a good first draft and a bad final layout. A pointer-drag on a card
1913
+ // writes a {dx,dy} for that `data-node-id`; a corner handle on a containment
1914
+ // box writes {dw,dh} (and its title drags the box). The deltas live in
1915
+ // localStorage per project + lens (web/layout-store.js) and are re-applied at
1916
+ // the end of every render(), so the graph underneath stays chant's and a
1917
+ // delta for a node that left the estate is simply not applied.
1918
+ //
1919
+ // What this does NOT do, said plainly rather than faked:
1920
+ // * a resized box does not reflow its children — that is dagre's job on the
1921
+ // next layout, and the reset control's tooltip says so;
1922
+ // * an edge touching a displaced node is redrawn as a STRAIGHT line between
1923
+ // its original anchor points, each shifted by its own node's delta. #228
1924
+ // accepts the straight-line fallback; spline re-routing is pinhole's job.
1925
+ // An edge with both ends where dagre put them keeps its bezier untouched.
1926
+ // * nothing is written to the server. The second tier of #228 (a
1927
+ // `.behold/layout.json` sidecar behind POST /api/layout, so a server-side
1928
+ // export honours the same deltas) is the follow-up half.
1929
+ let layoutIndex = null; // {nodes,boxes,edges} for the SVG currently on screen
1930
+ let layoutDeltas = {}; // the deltas in force for the current key
1931
+ let layoutDrag = null; // the gesture in flight
1932
+ let layoutWired = false;
1933
+
1934
+ /** The storage key for the project + lens on screen; null before /api/project lands. */
1935
+ function currentLayoutKey() {
1936
+ if (!projectInfo) return null;
1937
+ return layoutKey(projectKeyOf(projectInfo), lensKeyOf({ zoom: zoomValue(), radial: view.radial, stack: view.stack }));
1938
+ }
1939
+
1940
+ /** First and last coordinate pair of a path `d` — pinhole's own edge anchors. */
1941
+ function pathAnchors(d) {
1942
+ const n = String(d || "").match(/-?\d*\.?\d+(?:e[-+]?\d+)?/gi);
1943
+ if (!n || n.length < 4) return null;
1944
+ return { sx: +n[0], sy: +n[1], ex: +n[n.length - 2], ey: +n[n.length - 1] };
1945
+ }
1946
+
1947
+ /**
1948
+ * Wrap each containment box in a `<g data-layout-box>` and give it a corner
1949
+ * handle, once per rendered SVG.
1950
+ *
1951
+ * FRAGILE BY CONSTRUCTION, and knowingly so: pinhole's `Canvas.groupBox` emits
1952
+ * a bare `<rect rx=…>` immediately followed by its title `<text>`, as a direct
1953
+ * child of the `<svg>` — no group, no `data-group-id` to match on. So behold
1954
+ * matches the structure, exactly the way addGitlabWaveBadges already has to.
1955
+ * The discriminators: a positive `rx` (the two page-background rects have
1956
+ * none), a `<text>` as the next non-badge sibling, and svg-root parentage (a
1957
+ * card's rect lives inside `[data-node-id]`, an edge-label's inside its own
1958
+ * `<g>`). The real fix is upstream — a `data-group-id` on the box — and until
1959
+ * pinhole stamps one, prefer the attribute here the moment it exists.
1960
+ */
1961
+ function wrapContainmentBoxes(svgEl) {
1962
+ const boxes = new Map();
1963
+ const handleSize = Math.max(12, Math.round((vbInit ? vbInit[2] : 1000) / 90));
1964
+ for (const rect of [...svgEl.children]) {
1965
+ if (rect.tagName.toLowerCase() !== "rect") continue;
1966
+ const x = parseFloat(rect.getAttribute("x"));
1967
+ const y = parseFloat(rect.getAttribute("y"));
1968
+ const w = parseFloat(rect.getAttribute("width"));
1969
+ const h = parseFloat(rect.getAttribute("height"));
1970
+ if (!(parseFloat(rect.getAttribute("rx")) > 0) || !(w > 0) || !(h > 0) || Number.isNaN(x) || Number.isNaN(y)) continue;
1971
+ // Collect rect → title, stepping over anything already stamped between
1972
+ // them (the GitLab wave badge), and bail at the next box or card.
1973
+ const members = [rect];
1974
+ let title = null;
1975
+ for (let el = rect.nextElementSibling; el; el = el.nextElementSibling) {
1976
+ const tag = el.tagName.toLowerCase();
1977
+ if (tag === "rect" || (tag === "g" && (el.hasAttribute("data-node-id") || el.hasAttribute("data-edge-from")))) break;
1978
+ members.push(el);
1979
+ if (tag === "text") {
1980
+ title = el;
1981
+ break;
1982
+ }
1983
+ }
1984
+ if (!title) continue;
1985
+ const id = `box:${(title.textContent || "").trim() || boxes.size}`;
1986
+ if (boxes.has(id)) continue;
1987
+ const g = document.createElementNS(SVGNS, "g");
1988
+ g.setAttribute("data-layout-box", id);
1989
+ rect.parentNode.insertBefore(g, rect);
1990
+ members.forEach((m) => g.appendChild(m));
1991
+ // The title doubles as the box's move handle — the interior stays pan
1992
+ // territory, which on a logical view is most of the canvas.
1993
+ title.setAttribute("data-layout-move", id);
1994
+ title.setAttribute("cursor", "move");
1995
+ const handle = document.createElementNS(SVGNS, "g");
1996
+ handle.setAttribute("data-layout-resize", id);
1997
+ handle.setAttribute("cursor", "nwse-resize");
1998
+ handle.setAttribute("opacity", "0"); // CSS reveals it on hover; a static export never shows it
1999
+ const grip = document.createElementNS(SVGNS, "rect");
2000
+ grip.setAttribute("width", String(handleSize));
2001
+ grip.setAttribute("height", String(handleSize));
2002
+ grip.setAttribute("rx", "3");
2003
+ grip.setAttribute("fill", "var(--focus)");
2004
+ const tip = document.createElementNS(SVGNS, "title");
2005
+ tip.textContent = "Drag to resize this box. Children don't move with it — that's the next layout's job.";
2006
+ handle.append(tip, grip);
2007
+ g.appendChild(handle);
2008
+ boxes.set(id, { g, rect, handle, x, y, w0: w, h0: h, size: handleSize });
2009
+ }
2010
+ return boxes;
2011
+ }
2012
+
2013
+ /** Park the corner handle at the box's current bottom-right. */
2014
+ function positionBoxHandle(b, w, h) {
2015
+ b.handle.setAttribute("transform", `translate(${b.x + w - b.size - 3}, ${b.y + h - b.size - 3})`);
2016
+ }
2017
+
2018
+ /** Index the freshly rendered SVG: node groups, containment boxes, edge paths. */
2019
+ function indexLayout(svgEl) {
2020
+ const nodes = new Map();
2021
+ for (const el of svgEl.querySelectorAll("[data-node-id]")) {
2022
+ const id = el.getAttribute("data-node-id");
2023
+ if (!nodes.has(id)) nodes.set(id, { el, base: el.getAttribute("transform") || "" });
2024
+ }
2025
+ const edges = [];
2026
+ for (const g of svgEl.querySelectorAll("g[data-edge-from]")) {
2027
+ const paths = [...g.querySelectorAll("path")];
2028
+ if (!paths.length) continue;
2029
+ const d0 = paths[0].getAttribute("d");
2030
+ edges.push({ from: g.getAttribute("data-edge-from"), to: g.getAttribute("data-edge-to"), paths, d0, anchors: pathAnchors(d0) });
2031
+ }
2032
+ layoutIndex = { nodes, boxes: wrapContainmentBoxes(svgEl), edges };
2033
+ }
2034
+
2035
+ /** Paint `layoutDeltas` onto the indexed SVG. Idempotent: always from the original. */
2036
+ function renderLayout() {
2037
+ if (!layoutIndex) return;
2038
+ for (const [id, n] of layoutIndex.nodes) {
2039
+ const d = layoutDeltas[id];
2040
+ const t = d && (d.dx || d.dy) ? `translate(${d.dx || 0}, ${d.dy || 0}) ${n.base}`.trim() : n.base;
2041
+ if (t) n.el.setAttribute("transform", t);
2042
+ else n.el.removeAttribute("transform");
2043
+ }
2044
+ for (const [id, b] of layoutIndex.boxes) {
2045
+ const d = layoutDeltas[id] || {};
2046
+ const w = Math.max(b.size * 3, b.w0 + (d.dw || 0));
2047
+ const h = Math.max(b.size * 3, b.h0 + (d.dh || 0));
2048
+ b.rect.setAttribute("width", String(w));
2049
+ b.rect.setAttribute("height", String(h));
2050
+ if (d.dx || d.dy) b.g.setAttribute("transform", `translate(${d.dx || 0}, ${d.dy || 0})`);
2051
+ else b.g.removeAttribute("transform");
2052
+ positionBoxHandle(b, w, h);
2053
+ }
2054
+ for (const e of layoutIndex.edges) {
2055
+ const a = layoutDeltas[e.from];
2056
+ const z = layoutDeltas[e.to];
2057
+ if ((!a && !z) || !e.anchors) {
2058
+ if (e.paths[0].getAttribute("d") !== e.d0) e.paths.forEach((p) => p.setAttribute("d", e.d0));
2059
+ continue;
2060
+ }
2061
+ const { sx, sy, ex, ey } = e.anchors;
2062
+ const d = `M ${sx + ((a && a.dx) || 0)} ${sy + ((a && a.dy) || 0)} L ${ex + ((z && z.dx) || 0)} ${ey + ((z && z.dy) || 0)}`;
2063
+ e.paths.forEach((p) => p.setAttribute("d", d));
2064
+ }
2065
+ }
2066
+
2067
+ /** Re-index the SVG, load this lens's deltas, paint them. Called from render(). */
2068
+ function applyLayout() {
2069
+ const svgEl = currentSvg();
2070
+ const host = document.getElementById("graph");
2071
+ if (!svgEl || !host) return;
2072
+ indexLayout(svgEl);
2073
+ const key = currentLayoutKey();
2074
+ // Stale ids are dropped on apply, not on write: a lens the user hasn't
2075
+ // opened in a while shouldn't have its deltas quietly deleted because this
2076
+ // render happened to be a different projection of the same estate.
2077
+ layoutDeltas = key ? applicable(readLayout(localStorage, key), [...layoutIndex.nodes.keys(), ...layoutIndex.boxes.keys()]) : {};
2078
+ renderLayout();
2079
+ ensureLayoutReset(host);
2080
+ ensureLayoutDrag(host);
2081
+ }
2082
+
2083
+ /** The grabbable thing at or above `el`, if any. */
2084
+ function layoutTargetIn(el) {
2085
+ if (!el || typeof el.closest !== "function") return null;
2086
+ const resize = el.closest("[data-layout-resize]");
2087
+ if (resize) return { kind: "resize", id: resize.getAttribute("data-layout-resize") };
2088
+ const move = el.closest("[data-layout-move]");
2089
+ if (move) return { kind: "move", id: move.getAttribute("data-layout-move") };
2090
+ const node = el.closest("[data-node-id]");
2091
+ if (node) return { kind: "move", id: node.getAttribute("data-node-id") };
2092
+ return null;
2093
+ }
2094
+
2095
+ /** What (if anything) a pointerdown grabbed. Also the pan handler's bail test. */
2096
+ function layoutTargetOf(e) {
2097
+ const direct = layoutTargetIn(e.target);
2098
+ if (direct) return direct;
2099
+ // An edge can swallow the grab: wireEdgeHighlight raises every edge on a
2100
+ // hovered card to the top of the SVG, and pinhole's 14-wide transparent
2101
+ // hit-path is anchored at the card's own centre — so by the time the pointer
2102
+ // is down, the edge you just lit is lying across the card you meant to move.
2103
+ // Look through the stack for the card underneath instead of giving up.
2104
+ if (!e.target || typeof e.target.closest !== "function" || !e.target.closest("g[data-edge-from]")) return null;
2105
+ for (const el of document.elementsFromPoint(e.clientX, e.clientY)) {
2106
+ const t = layoutTargetIn(el);
2107
+ if (t) return t;
2108
+ }
2109
+ return null;
2110
+ }
2111
+
2112
+ /** Screen pixels → viewBox units at the current zoom. */
2113
+ function userPerPixel(svgEl) {
2114
+ const m = svgEl.getScreenCTM && svgEl.getScreenCTM();
2115
+ if (m && m.a && m.d) return { x: 1 / m.a, y: 1 / m.d };
2116
+ const r = svgEl.getBoundingClientRect();
2117
+ return vb && r.width && r.height ? { x: vb[2] / r.width, y: vb[3] / r.height } : { x: 1, y: 1 };
2118
+ }
2119
+
2120
+ function ensureLayoutDrag(host) {
2121
+ if (layoutWired) return;
2122
+ layoutWired = true;
2123
+ host.addEventListener("pointerdown", (e) => {
2124
+ if (e.button !== 0 || !layoutIndex) return;
2125
+ const t = layoutTargetOf(e);
2126
+ if (!t || !currentSvg()) return;
2127
+ layoutDrag = { ...t, x0: e.clientX, y0: e.clientY, base: layoutDeltas[t.id] || {}, moved: false };
2128
+ // The same latch pan uses (wire()'s click-inspect bails on it): claim it on
2129
+ // grab so a drag is never also a click, and so the NEXT plain click on a
2130
+ // node isn't swallowed by a stale `true` from the gesture before it.
2131
+ panMoved = false;
2132
+ // Deliberately no preventDefault(): cancelling pointerdown suppresses the
2133
+ // compatibility mouse events, and click-inspect rides on those. The pan is
2134
+ // held off by its own guard instead, and the text selection a drag would
2135
+ // otherwise smear across the graph by `user-select: none` on the SVG.
2136
+ });
2137
+ window.addEventListener("pointermove", (e) => {
2138
+ if (!layoutDrag) return;
2139
+ const svgEl = currentSvg();
2140
+ if (!svgEl) return;
2141
+ const px = e.clientX - layoutDrag.x0;
2142
+ const py = e.clientY - layoutDrag.y0;
2143
+ if (!layoutDrag.moved && Math.abs(px) + Math.abs(py) <= 3) return;
2144
+ layoutDrag.moved = true;
2145
+ panMoved = true;
2146
+ const s = userPerPixel(svgEl);
2147
+ const dx = px * s.x;
2148
+ const dy = py * s.y;
2149
+ const b = layoutDrag.base;
2150
+ const next =
2151
+ layoutDrag.kind === "resize"
2152
+ ? { dx: b.dx || 0, dy: b.dy || 0, dw: (b.dw || 0) + dx, dh: (b.dh || 0) + dy }
2153
+ : { dx: (b.dx || 0) + dx, dy: (b.dy || 0) + dy, dw: b.dw || 0, dh: b.dh || 0 };
2154
+ layoutDeltas = setDelta(layoutDeltas, layoutDrag.id, next);
2155
+ renderLayout();
2156
+ });
2157
+ window.addEventListener("pointerup", () => {
2158
+ if (!layoutDrag) return;
2159
+ const moved = layoutDrag.moved;
2160
+ layoutDrag = null;
2161
+ if (!moved) return;
2162
+ const key = currentLayoutKey();
2163
+ if (key) writeLayout(localStorage, key, layoutDeltas);
2164
+ ensureLayoutReset(document.getElementById("graph"));
2165
+ });
2166
+ }
2167
+
2168
+ /** The "↺ layout" control beside ⤢ fit — shown only while something is hand-placed. */
2169
+ function ensureLayoutReset(host) {
2170
+ if (!host) return;
2171
+ let btn = document.getElementById("layout-reset");
2172
+ if (!btn || btn.parentElement !== host) {
2173
+ btn = document.createElement("button");
2174
+ btn.id = "layout-reset";
2175
+ btn.textContent = "↺ layout";
2176
+ btn.title =
2177
+ "Drop the hand-placed layout for this project + this lens and go back to dagre's. " +
2178
+ "(A resized box never reflows what's inside it — only the next layout does that.)";
2179
+ btn.addEventListener("click", (e) => {
2180
+ e.stopPropagation();
2181
+ const key = currentLayoutKey();
2182
+ if (key) clearLayout(localStorage, key);
2183
+ layoutDeltas = {};
2184
+ renderLayout();
2185
+ ensureLayoutReset(host);
2186
+ nowline("↺ layout reset — back to dagre's placement");
2187
+ });
2188
+ host.appendChild(btn);
2189
+ }
2190
+ btn.style.display = isEmpty(layoutDeltas) ? "none" : "";
2191
+ }
2192
+
1865
2193
  // Precondition-failure codes (#72) → a short, human title for the entry/error
1866
2194
  // screen below. Mirrors every `code` a read route's structured error can
1867
2195
  // carry (src/server.ts RouteErrorCode: chant.ts's lint/not-installed/eval,
@@ -2238,7 +2566,7 @@ function showToast(msg, ok) {
2238
2566
  }
2239
2567
  const t = document.createElement("div");
2240
2568
  const color = ok ? "var(--managed)" : "var(--degraded)";
2241
- t.style.cssText = `background:var(--panel);color:var(--fg);border:1px solid ${color};border-left:4px solid ${color};border-radius:8px;padding:8px 12px;font-size:12px;box-shadow:0 4px 16px rgba(0,0,0,.35);cursor:pointer;white-space:pre-wrap`;
2569
+ t.style.cssText = `background:var(--panel);color:var(--fg);border:1px solid ${color};border-left:4px solid ${color};border-radius:var(--r-ctl);padding:8px 12px;font-size:var(--t-body);box-shadow:0 6px 18px color-mix(in srgb, var(--shadow) 50%, transparent);cursor:pointer;white-space:pre-wrap`;
2242
2570
  t.textContent = msg;
2243
2571
  t.onclick = () => t.remove();
2244
2572
  host.appendChild(t);
@@ -2319,7 +2647,7 @@ async function openRollback(btn) {
2319
2647
  wrap.style.cssText = "display:flex;gap:6px;align-self:center";
2320
2648
  const sel = document.createElement("select");
2321
2649
  sel.style.cssText =
2322
- "background:var(--panel);color:var(--fg);border:1px solid var(--line);border-radius:6px;padding:4px 8px;font-size:12px;max-width:340px";
2650
+ "background:var(--well);color:var(--fg);border:1px solid var(--line);border-radius:var(--r-ctl);padding:4px 8px;font-size:var(--t-body);max-width:340px";
2323
2651
  for (const c of commits) sel.add(new Option(`${c.sha} · ${c.subject} (${c.date})`, c.sha));
2324
2652
  const go = button("Roll back →", "", () => {
2325
2653
  const to = sel.value;
@@ -2363,7 +2691,7 @@ async function initActions() {
2363
2691
  const pill = document.createElement("span");
2364
2692
  pill.textContent = `● static snapshot${manifest && manifest.capturedAt ? " · " + manifest.capturedAt.slice(0, 16).replace("T", " ") : ""}`;
2365
2693
  pill.title = "An exported, read-only snapshot — no live observe or deploy.";
2366
- pill.style.cssText = "align-self:center;font-size:11px;color:var(--muted);border:1px solid var(--line);border-radius:6px;padding:2px 8px";
2694
+ pill.style.cssText = "align-self:center;font:var(--t-caption)/1.4 var(--font-mono);color:var(--muted);border:1px solid var(--line);border-radius:var(--r-ctl);padding:2px 8px";
2367
2695
  bar.appendChild(pill);
2368
2696
  previewMode = true;
2369
2697
  return; // nothing else in the bar is a read
@@ -2395,7 +2723,7 @@ async function initActions() {
2395
2723
  local.emulators.map((e) => `${e.lexicon} ${e.name} @ ${e.endpoint}`).join("; ") +
2396
2724
  ". Deploys and the overlay observe them — no cloud creds.";
2397
2725
  pill.style.cssText =
2398
- "align-self:center;font-size:11px;color:var(--managed);border:1px solid var(--managed);border-radius:6px;padding:2px 8px";
2726
+ "align-self:center;font:var(--t-caption)/1.4 var(--font-mono);color:var(--managed);border:1px solid var(--managed);border-radius:var(--r-ctl);padding:2px 8px";
2399
2727
  bar.appendChild(pill);
2400
2728
  }
2401
2729
  // Auto-sync banner (#29) — make an active self-heal loop visible, not silent.
@@ -2404,7 +2732,7 @@ async function initActions() {
2404
2732
  pill.textContent = `⟳ auto-sync: ${autoSync}`;
2405
2733
  pill.title = `On polled drift, behold triggers the ${autoSync === "apply" ? "ApplyOp (heal)" : "ReconcileOp (adopt)"}. Gated applies still wait for Approve.`;
2406
2734
  pill.style.cssText =
2407
- "align-self:center;font-size:11px;color:var(--pending);border:1px solid var(--pending);border-radius:6px;padding:2px 8px";
2735
+ "align-self:center;font:var(--t-caption)/1.4 var(--font-mono);color:var(--pending);border:1px solid var(--pending);border-radius:var(--r-ctl);padding:2px 8px";
2408
2736
  bar.appendChild(pill);
2409
2737
  }
2410
2738
  opsApply = ops.find((o) => o.kind === "apply") ?? null;
@@ -2451,7 +2779,7 @@ async function initActions() {
2451
2779
  // its deploy path is Apply all, not committed Ops).
2452
2780
  if (ops.length === 0 && !previewMode && !opsInitialEnv) {
2453
2781
  const hint = document.createElement("span");
2454
- hint.style.cssText = "color:var(--muted);font-size:11px;align-self:center";
2782
+ hint.style.cssText = "color:var(--muted);font-size:var(--t-caption);align-self:center";
2455
2783
  hint.textContent = "no Ops — commit an *.op.ts (ApplyOp / ReconcileOp / any deploy Op) to act";
2456
2784
  hint.title = "behold triggers committed Ops on your executor. Add one to enable Deploy / Adopt / Run.";
2457
2785
  bar.appendChild(hint);
@@ -2520,7 +2848,7 @@ events.addEventListener("pr", (e) => {
2520
2848
  slot.id = "pr-link";
2521
2849
  slot.target = "_blank";
2522
2850
  slot.rel = "noopener";
2523
- slot.style.cssText = "color:var(--managed);text-decoration:none;font-size:12px;align-self:center";
2851
+ slot.style.cssText = "color:var(--managed);text-decoration:none;font-size:var(--t-body);align-self:center";
2524
2852
  document.getElementById("actions").after(slot);
2525
2853
  }
2526
2854
  slot.href = url;
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" viewBox="0 0 581 747"><path id="cncf-argo-a" fill="#fff" d="M336.76 437.61c0 28.31-18.83 43.36-49.29 43.36-30.45 0-47.2-17.84-47.2-43.36 0 0 16.75 23.65 47.2 23.65 30.46 0 49.3-23.65 49.3-23.65Z"/><path fill="#E2F5FC" d="M290.5 547.98c148.47 0 268.84-120.45 268.84-269.04 0-148.58-120.37-269.03-268.84-269.03-148.47 0-268.84 120.45-268.84 269.03 0 148.59 120.37 269.04 268.84 269.04Z"/><path fill="#CDECF6" d="M290.5 504.04c121.36 0 219.74-98.45 219.74-219.9S411.86 64.24 290.5 64.24c-121.36 0-219.74 98.45-219.74 219.9s98.38 219.9 219.74 219.9Z"/><path fill="#fff" d="M208.31 91.51a12.98 12.98 0 1 0 0-25.96 12.98 12.98 0 0 0 0 25.96Zm-96.57 325.82a4.5 4.5 0 1 0 7.08-5.54c-87.8-112.21-37.15-257.65 62.5-314.74a4.5 4.5 0 0 0-4.47-7.8C73.23 148.6 20.03 300.12 111.74 417.33Z"/><path fill="#B5D2F3" d="M19.26 225.54a3.5 3.5 0 1 0-7 0h7Zm-7 105.38a3.5 3.5 0 1 0 7 0h-7Zm555.07-105.88a3.5 3.5 0 1 0-7 0h7Zm-7 105.38a3.5 3.5 0 1 0 7 0h-7ZM289.79 555.97c153.28 0 277.54-124.35 277.54-277.73h-7c0 149.52-121.13 270.73-270.54 270.73v7Zm277.54-277.73C567.33 124.84 443.07.5 289.79.5v7c149.41 0 270.54 121.21 270.54 270.74h7ZM289.79.5C136.51.5 12.26 124.85 12.26 278.24h7C19.26 128.7 140.38 7.5 289.79 7.5v-7ZM12.26 278.24c0 153.38 124.25 277.73 277.53 277.73v-7c-149.4 0-270.53-121.2-270.53-270.73h-7Zm0-52.7v105.38h7V225.54h-7Zm548.07-.5v105.38h7V225.04h-7Z"/><path fill="#B5D2F3" d="M21.66 225.73a10.84 10.84 0 1 0-21.66 0v106.43a10.84 10.84 0 1 0 21.66 0V225.73Zm559.34 0a10.84 10.84 0 1 0-21.66 0v106.43a10.84 10.84 0 1 0 21.66 0V225.73Z"/><path fill="#FE733E" d="M171.17 687.76c12.53-5.1 9.98-10.2 9.98-12.74l-6.1-92.55-5.37-46.3-8.12-289.07c0-68.6 50.73-132.72 127.3-132.72s130.76 61.71 130.88 132.77c-2.2 137.24-5.9 189.47-11.07 288.02-.63 13.47-2.38 33.86-2.96 46.32-2.88 61.66-6.14 91.87-6.14 93.53 0 2.55-2.54 7.64 9.98 12.74 12.52 5.1 41.46 11.71 41.46 13.33 0 1.6-48.89 0-48.89 0-28.02 0-28.02-20.98-28.02-26.07 0-5.1-7.64-92.6-7.64-92.6l-2.55 112.99c0 5.1 2.55 12.74 20.38 17.84 0 0 38.91 5.89 38.91 8.01 0 2.13-46.55 0-46.55 0-35.45 0-35.45-20.75-35.45-20.75l-7.64-92.6S331 700.5 331 713.24c0 9.99 1.73 17.63 29.54 22.73 13.43 3.73 40.54 8.35 40.54 10.2 0 1.85-61.86 0-61.86 0-33.11-2.55-36.24-25.5-36.24-25.5l-12.55-46.14-12.5 46.15s-5.09 22.94-38.2 25.49c0 0-56.17 1.85-56.17 0s22.5-5.99 36.81-10.2c19.36-5.69 29.55-12.74 29.55-22.73 0-12.74-2.55-105.35-2.55-105.35l-7.64 92.6s0 22.74-35.45 22.74c0 0-50.5.15-50.5-1.98 0-2.12 42.64-8 42.64-8 17.83-5.1 20.38-12.75 20.38-17.85l-2.55-113s-7.64 87.5-7.64 92.6c0 5.1 0 28.05-28.02 28.05 0 0-47.13-2.33-47.13-3.95 0-1.6 27.18-6.25 39.7-11.35Z"/><path fill="#FE6446" d="M169.68 536.16s-5.23 12.8-17.05 19.7c-11.81 6.9-25.51 9.14-38.4 10.84 0 0 17.72 8.87 39.39 6.9 13.58-1.1 19.28 2.36 21.42 8.87l-5.36-46.3Zm238.99-.99s7.63 12.8 19.45 19.7c11.82 6.9 25.51 9.15 38.4 10.85 0 0-17.72 8.87-39.38 6.9-13.58-1.1-19.29 2.35-21.43 8.87l2.96-46.32Z"/><path fill="#FE6446" d="m408.81 533 .99-16.02c-62.56 29.54-120.7 26.43-120.7 26.43s-58.49 2.45-119.92-26.57l.4 15.57s43.82 26.94 119.52 26.94c75.7 0 119.71-26.35 119.71-26.35Z" opacity=".3"/><path fill="#FEA777" d="M238.78 194.25a17.84 17.84 0 1 0-.01-35.67 17.84 17.84 0 0 0 .01 35.67Z" opacity=".5"/><path fill="#FE6B3C" d="M419.5 249.06s1.88-22.34-7.88-48.94c-26.77-73.01-92.56-87.71-128.02-85.64 0 0 50.28 23.76 52.61 99.8 1.06 33.72 1.06 34.78 1.06 34.78h82.23Z"/><use xlink:href="#cncf-argo-a"/><use xlink:href="#cncf-argo-a"/><path fill="#fff" d="M336.76 437.61c0 28.31-11.39 72.92-49.29 72.92s-47.2-47.4-47.2-72.92c0 0 0 43.36 47.2 43.36 51.62 0 49.3-43.36 49.3-43.36Z"/><path fill="#070909" d="M336.76 437.61c0 28.31-11.39 72.92-49.29 72.92s-47.2-47.4-47.2-72.92c0 0 0 43.36 47.2 43.36 51.62 0 49.3-43.36 49.3-43.36Z"/><path fill="#FE6446" d="M197.93 394.25a77.82 77.82 0 0 0 77.8-77.85c0-43-34.83-77.85-77.8-77.85a77.82 77.82 0 0 0-77.8 77.85c0 43 34.84 77.85 77.8 77.85Z"/><path fill="#fff" d="M197.73 378.32a60.94 60.94 0 0 0 60.92-60.96 60.94 60.94 0 0 0-60.92-60.97 60.94 60.94 0 0 0-60.92 60.97 60.94 60.94 0 0 0 60.92 60.96Z"/><path fill="#090B0B" d="M195.96 325.27a18.72 18.72 0 1 0-.01-37.44 18.72 18.72 0 0 0 .01 37.44Z"/><path fill="#FE6446" d="M381.1 394.25a77.82 77.82 0 0 0 77.79-77.85c0-43-34.83-77.85-77.8-77.85a77.82 77.82 0 0 0-77.79 77.85c0 43 34.83 77.85 77.8 77.85Z"/><path fill="#fff" d="M378 378.21a60.94 60.94 0 0 0 60.93-60.96A60.94 60.94 0 0 0 378 256.29a60.94 60.94 0 0 0-60.92 60.96A60.94 60.94 0 0 0 378 378.21Z"/><path fill="#090B0B" d="M379.13 325.27a18.72 18.72 0 1 0-.02-37.44 18.72 18.72 0 0 0 .01 37.44Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" role="img" viewBox="48.00 -2.25 262.50 364.00"><defs><style>.cncf-flux-cls-1{fill:#326ce5}.cncf-flux-cls-3{fill:none}</style></defs><path d="M59.72393 97.77847a10.18251 10.18251 0 0 1 0-17.07393l114.0703-74.16023a10.18258 10.18258 0 0 1 11.10024 0l114.07029 74.16023a10.1825 10.1825 0 0 1 0 17.07394l-114.07029 74.16021a10.18254 10.18254 0 0 1-11.10024 0z" class="cncf-flux-cls-1"/><path fill="#c1d2f7" d="M197.35565 110.866h7.91277a6.00326 6.00326 0 0 0 5.19908-9.005l-25.92381-44.902a6.00355 6.00355 0 0 0-10.39838 0l-25.924 44.902a6.00326 6.00326 0 0 0 5.19905 9.005h7.913a6.0034 6.0034 0 0 1 6.00341 6.0034v51.2563l5.31037 3.45252a12.28743 12.28743 0 0 0 13.39445 0l5.31062-3.45252v-51.25628a6.0034 6.0034 0 0 1 6.0034-6.00341z"/><path d="M173.79348 353.27108a10.12828 10.12828 0 0 0 3.45643 1.402c-2.17549-1.2312-4.38727-2.37429-6.62269-3.46036zm-6.45667-161.19546l-11.3157 7.35661a110.993 110.993 0 0 0 11.31571 6.5997zm24.01537 23.71607c9.73858 3.11505 19.81317 5.64851 30.10973 8.18655 10.92032 2.69182 21.97355 5.42717 32.81792 9.00839l-35.41606-23.02482a215.88133 215.88133 0 0 1-27.51161-9.41007zm0 62.19159v.58577c0 2.56929-2.68787 4.65195-6.00338 4.65195h-12.00855c-3.31549 0-6.00338-2.08266-6.00338-4.65195v-6.60473c-24.90538-6.40209-49.87294-14.48506-70.85945-33.8211L81.94819 247.589c22.46761 22.0509 50.27893 28.93291 79.71441 36.1886 27.17852 6.69856 55.18051 13.65144 78.68557 33.44222l14.67907-9.54334c-18.53142-16.50431-40.40962-23.68778-63.67506-29.6932zm-98.03756 22.96639l38.90829 25.29525c20.06438 5.11336 40.20029 11.24773 58.311 23.36044l14.90023-9.687c-19.7518-13.6724-42.5505-19.3134-66.41889-25.197-15.28704-3.76779-30.83479-7.6166-45.70063-13.77169zm74.02219-82.25413a124.3199 124.3199 0 0 1-21.53392-12.62006l-14.80531 9.62521a124.54057 124.54057 0 0 0 36.33923 18.708zm51.41006 16.29412c-9.1062-2.24474-18.30362-4.52388-27.39469-7.29853v13.68209q5.051 1.2853 10.17672 2.53817c29.93438 7.37818 60.8879 15.00885 85.70767 39.82861.60979.60979 1.16261 1.23989 1.752 1.85715l9.97427-6.48445a10.10425 10.10425 0 0 0 3.47529-3.79928c-1.06644-1.17252-2.144-2.34132-3.28875-3.48612-22.58513-22.5853-50.66938-29.50856-80.40251-36.83764zm-19.93295 19.93298q-3.72217-.91747-7.46168-1.85484v13.20974c25.81676 6.56181 51.80263 14.68843 73.4551 35.04069l14.52114-9.44055c-.03854-.03875-.07256-.07916-.11131-.11792-22.58546-22.58524-50.66975-29.50848-80.40325-36.83712zm-31.47711-8.58681c-16.28-5.27813-32.11777-12.39682-46.344-24.13081l-14.68176 9.5451c17.87023 15.55727 38.7844 22.67266 61.02572 28.50068z" class="cncf-flux-cls-3"/><path d="M73.24049 254.95972c-.348-.348-.6585-.70949-.99967-1.05976l-12.51495 8.13648a10.16548 10.16548 0 0 0-1.64428 1.3968c1.05336 1.15677 2.11561 2.31024 3.24544 3.44 22.5852 22.58519 50.66968 29.50838 80.40322 36.83688 25.3425 6.24691 51.40113 12.71661 73.86454 29.60253l14.80574-9.62562c-20.86424-16.41993-45.53021-22.50891-71.45287-28.898-29.93385-7.37888-60.88738-15.00944-85.70717-39.82931z" class="cncf-flux-cls-3"/><path d="M218.86377 209.9618l-27.51159-17.886v8.4759a215.88 215.88 0 0 0 27.51159 9.4101zm-27.51159 5.82989v11.89938c9.09108 2.77471 18.28849 5.05388 27.39469 7.29853 29.73311 7.32909 57.81738 14.25232 80.40261 36.83773 1.14474 1.14474 2.22232 2.31354 3.28875 3.48611a10.19327 10.19327 0 0 0-3.4753-13.27711l-44.6831-29.04976c-10.84437-3.58121-21.89768-6.31656-32.81794-9.00838-10.29656-2.53799-20.37113-5.07145-30.10971-8.1865zm-24.01537-9.75969a110.99292 110.99292 0 0 1-11.31571-6.59969l-10.21821 6.6432a124.31932 124.31932 0 0 0 21.53392 12.62007zm34.19212 37.87941q-5.12528-1.26352-10.17673-2.53816v11.6946q3.73686.938 7.46168 1.85485c29.73352 7.32863 57.8178 14.25187 80.40318 36.83709.03876.03876.07277.07916.11132.11792l9.66023-6.28051c-.58945-.61726-1.14225-1.24733-1.752-1.85715-24.81978-24.81979-55.7733-32.45048-85.70768-39.82864zm-34.19212-9.50272a124.54057 124.54057 0 0 1-36.33923-18.708l-10.00473 6.50436c14.22612 11.734 30.06393 18.85268 46.344 24.1308zm0 25.84203c-22.24132-5.828-43.1555-12.94341-61.02572-28.5006l-9.8338 6.39315c20.98652 19.336 45.95407 27.41893 70.85947 33.8211zm24.01537 17.73256c23.26546 6.00539 45.14367 13.18886 63.67506 29.69312l9.78-6.35823c-21.65249-20.35225-47.63835-28.47893-73.45509-35.04068zM81.94813 247.589l-9.70729 6.311c.34117.35032.65166.71178.99967 1.05976 24.81977 24.81979 55.77329 32.45043 85.70706 39.82925 25.92266 6.38909 50.58863 12.47807 71.45288 28.898l9.94772-6.46724c-23.50506-19.79078-51.507-26.74365-78.68557-33.44222-29.43548-7.25569-57.2468-14.1377-79.71447-36.18855zM61.327 266.87321c-1.12983-1.12983-2.19209-2.28328-3.24545-3.44a10.1544 10.1544 0 0 0 1.64428 15.67957l33.5887 21.83694c14.86583 6.15509 30.41359 10.0039 45.70058 13.77166 23.8684 5.88357 46.66708 11.52464 66.4189 25.197l10.1606-6.6057c-22.46332-16.88587-48.522-23.35562-73.86447-29.60253-29.73343-7.32851-57.81791-14.25175-80.40314-36.83694zm109.30024 84.33948c2.2354 1.0861 4.44718 2.22919 6.62269 3.46036a10.16012 10.16012 0 0 0 7.64543-1.402l5.63858-3.66577c-18.11065-12.11266-38.24653-18.247-58.31095-23.36044z" class="cncf-flux-cls-1"/></svg>
@@ -0,0 +1 @@
1
+ <svg id="cncf-helm-Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><defs><style>.cncf-helm-cls-1{fill:#0f1689;}</style></defs><path class="cncf-helm-cls-1" d="M136.52938,121.13478c-.572-.54252-1.19462-1.12636-1.81-1.71761-12.61677-12.12115-22.38092-26.13637-28.279-42.702-1.65073-4.63637-2.89692-9.36515-2.67587-14.35871.021-.4739.01959-.94915.05226-1.42214.445-6.44594,4.75912-9.70322,11.05839-8.17669a27.325,27.325,0,0,1,5.73149,2.19653c6.89022,3.45538,12.5062,8.56359,17.67051,14.16571a112.52651,112.52651,0,0,1,21.722,33.42965,8.29635,8.29635,0,0,0,.38946.861c.07116.12855.22232.21282.55927.51883A176.357,176.357,0,0,1,241.968,79.06864c-.17651-.8761-.28195-1.54571-.44772-2.2a112.49436,112.49436,0,0,1-2.65292-36.95637,84.07478,84.07478,0,0,1,4.44444-21.76378,31.32555,31.32555,0,0,1,5.47651-10.17139,15.687,15.687,0,0,1,3.16336-2.82149,7.026,7.026,0,0,1,8.03255-.056,17.27854,17.27854,0,0,1,5.8402,6.73185A53.05435,53.05435,0,0,1,271.08657,26.508a112.50469,112.50469,0,0,1,2.12263,33.00356,95.59806,95.59806,0,0,1-3.49052,19.91081c7.12171,1.31193,14.20955,2.32869,21.1473,3.97713a186.37909,186.37909,0,0,1,20.44069,6.0033A188.31671,188.31671,0,0,1,331.0769,97.9721c6.34538,3.16433,12.38553,6.94066,18.71757,10.53829.20571-.433.50439-.94982.706-1.50212A108.65959,108.65959,0,0,1,383.40128,60.246a37.75787,37.75787,0,0,1,11.822-6.883,17.24558,17.24558,0,0,1,3.67827-.84512c6.264-.71729,8.89351,3.2244,9.35653,7.93183a29.94372,29.94372,0,0,1-.77381,10.35466A87.90551,87.90551,0,0,1,396.75426,95.492c-6.79016,10.97178-14.85015,20.85494-25.09307,28.83042-.30234.2354-.56784.51814-1.07988.99029a177.77993,177.77993,0,0,1,26.59293,30.88244,10.96227,10.96227,0,0,1-1.689.29762c-10.59546.015-21.1911-.01829-31.78622.04607a4.004,4.004,0,0,1-3.17246-1.69,147.87522,147.87522,0,0,0-88.17776-46.54846,143.35862,143.35862,0,0,0-30.27955-1.16923,146.40735,146.40735,0,0,0-82.53728,31.81054,140.06663,140.06663,0,0,0-16.97616,15.84186,4.72839,4.72839,0,0,1-3.86326,1.75742c-10.12056-.07028-20.24188-.035-30.36293-.03495h-2.15212c.618-2.408,6.84026-10.93786,13.88352-18.55281C125.31349,132.2744,130.87768,126.8839,136.52938,121.13478Z"/><path class="cncf-helm-cls-1" d="M394.52934,347.9123a176.63854,176.63854,0,0,1-23.97343,27.16338c.70941.59068,1.28594,1.07041,1.86212,1.55057A108.31456,108.31456,0,0,1,406.10131,424.772a34.61831,34.61831,0,0,1,2.202,14.42026,14.88544,14.88544,0,0,1-.74786,3.69206,7.20762,7.20762,0,0,1-8.15793,5.02308,22.23329,22.23329,0,0,1-6.76276-2.00629,51.23237,51.23237,0,0,1-9.18151-5.8151,107.59183,107.59183,0,0,1-32.936-46.7072c-.18746-.51334-.39218-1.0204-.72243-1.87691a194.65008,194.65008,0,0,1-25.01223,14.00774,181.66925,181.66925,0,0,1-26.6869,9.72442,187.55649,187.55649,0,0,1-28.3045,5.38805c.16807.84015.26446,1.5098.43745,2.15907a109.172,109.172,0,0,1,2.9708,36.44311,80.804,80.804,0,0,1-4.42286,22.4773,78.24971,78.24971,0,0,1-4.16475,8.74473,13.39,13.39,0,0,1-2.33865,2.97083c-3.98009,4.109-8.73225,4.144-12.61157-.07366a27.28012,27.28012,0,0,1-3.907-5.61776c-3.07685-5.77569-4.6604-12.056-5.791-18.46021a116.86329,116.86329,0,0,1-1.35893-26.465,94.4795,94.4795,0,0,1,2.88466-19.18513c.14009-.53269.268-1.0696.37134-1.61034.02629-.13754-.06342-.2973-.17067-.73825a176.12108,176.12108,0,0,1-80.96855-24.99386c-.40992.90921-.76206,1.67473-1.10168,2.44579a110.47729,110.47729,0,0,1-30.90112,41.42041,38.16071,38.16071,0,0,1-12.04706,6.95909,12.08987,12.08987,0,0,1-6.51516.70023,7.11858,7.11858,0,0,1-5.40329-4.4892c-1.41614-3.424-1.16526-6.985-.68438-10.51691a55.45267,55.45267,0,0,1,4.30768-14.25037A112.49985,112.49985,0,0,1,134.88761,380.779c.459-.43461.92981-.857,1.38087-1.29954a3.76029,3.76029,0,0,0,.36534-.65529,178.90466,178.90466,0,0,1-28.469-31.31672c.98458-.08018,1.64327-.18,2.30205-.1806,10.51436-.00976,21.029.02736,31.54284-.04358a4.70554,4.70554,0,0,1,3.70344,1.62615,146.94611,146.94611,0,0,0,39.40276,28.88494,139.94667,139.94667,0,0,0,49.70395,14.77367q70.68048,6.87067,121.59971-42.85452a7.64571,7.64571,0,0,1,5.99261-2.44347c9.80139.12121,19.60512.04986,29.408.04986h2.53353Z"/><path class="cncf-helm-cls-1" d="M350.73576,197.76167c2.78711,0,5.47037.18919,8.11487-.0501,2.9951-.271,5.139.8001,7.32354,2.81308,12.61275,11.62214,25.35732,23.10129,38.05863,34.62729.63855.57948,1.29039,1.14432,2.1101,1.8701.76445-.65718,1.48119-1.24318,2.16483-1.8655Q428.14636,217.27914,447.756,199.37a5.44772,5.44772,0,0,1,4.20358-1.64576c3.21888.13038,6.44673.03746,9.8412.03746V303.13036c-1.72309.50368-24.8756.60366-27.63911.0611v-53.362l-.53656-.25427c-9.01079,8.21784-18.0216,16.43564-27.23786,24.8408-9.22584-8.31119-18.34194-16.52347-27.458-24.73577l-.52449.19192c-.023,4.45359-.00774,8.90828-.01046,13.36233q-.00408,6.63942-.00058,13.27882v26.87211h-27.428C350.45161,301.61243,350.21289,203.72342,350.73576,197.76167Z"/><path class="cncf-helm-cls-1" d="M97.63436,197.88229h27.26325c.55054,1.75251.65821,102.97139.09469,105.525H97.70468c-.15021-6.70344-.04736-13.38394-.06622-20.06131-.01871-6.62261-.0041-13.24531-.0041-20.03958H63.84679V303.047c-2.05946.61518-25.33374.67417-27.64758.123V197.89436H63.73742v37.18876c1.96793.56844,30.92319.67339,33.872.12942.00818-2.97713.02-6.02646.024-9.0758q.0063-4.7433.00094-9.4866,0-4.625,0-9.25C97.63437,204.32179,97.63436,201.24346,97.63436,197.88229Z"/><path class="cncf-helm-cls-1" d="M157.5757,303.3683V198.1946c1.617-.52913,61.545-.73586,65.46219-.20457v22.41432c-.87869.063-1.7857.18341-2.69279.18454q-16.0086.01994-32.01725.00881l-2.96767,0v17.43353H218.7067V261.2229H185.65838c-.55339,1.98509-.70531,15.81681-.25622,19.64583.84488.05679,1.75.16952,2.6553.17059q16.00862.01863,32.01727.00822h2.96945V303.3683Z"/><path class="cncf-helm-cls-1" d="M254.28291,303.40855c-.50074-2.82232-.39948-103.60181.09747-105.51735h27.16208v77.76453c1.17173.06084,2.09122.14921,3.01077.15005q16.128.01468,32.2561.00658c.92562,0,1.85125,0,2.90733,0v27.59622Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="18.035334mm" height="17.500378mm" viewBox="0 0 18.035334 17.500378" version="1.1" id="k8s-c-role-svg13826" inkscape:version="0.91 r13725" sodipodi:docname="c-role.svg"><defs id="k8s-c-role-defs13820" /><g inkscape:label="Calque 1" inkscape:groupmode="layer" id="k8s-c-role-layer1" transform="translate(-0.99262638,-1.174181)"><g id="k8s-c-role-g70" transform="matrix(1.0148887,0,0,1.0148887,16.902146,-2.698726)"><path inkscape:export-ydpi="250.55" inkscape:export-xdpi="250.55" inkscape:export-filename="new.png" inkscape:connector-curvature="0" id="k8s-c-role-path3055" d="m -6.8492015,4.2724668 a 1.1191255,1.1099671 0 0 0 -0.4288818,0.1085303 l -5.8524037,2.7963394 a 1.1191255,1.1099671 0 0 0 -0.605524,0.7529759 l -1.443828,6.2812846 a 1.1191255,1.1099671 0 0 0 0.151943,0.851028 1.1191255,1.1099671 0 0 0 0.06362,0.08832 l 4.0508,5.036555 a 1.1191255,1.1099671 0 0 0 0.874979,0.417654 l 6.4961011,-0.0015 a 1.1191255,1.1099671 0 0 0 0.8749788,-0.416906 L 1.3818872,15.149453 A 1.1191255,1.1099671 0 0 0 1.5981986,14.210104 L 0.15212657,7.9288154 A 1.1191255,1.1099671 0 0 0 -0.45339794,7.1758396 L -6.3065496,4.3809971 A 1.1191255,1.1099671 0 0 0 -6.8492015,4.2724668 Z" style="fill:#326ce5;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path id="k8s-c-role-path3054-2-9" d="M -6.8523435,3.8176372 A 1.1814304,1.171762 0 0 0 -7.3044284,3.932904 l -6.1787426,2.9512758 a 1.1814304,1.171762 0 0 0 -0.639206,0.794891 l -1.523915,6.6308282 a 1.1814304,1.171762 0 0 0 0.160175,0.89893 1.1814304,1.171762 0 0 0 0.06736,0.09281 l 4.276094,5.317236 a 1.1814304,1.171762 0 0 0 0.92363,0.440858 l 6.8576188,-0.0015 a 1.1814304,1.171762 0 0 0 0.9236308,-0.44011 l 4.2745966,-5.317985 a 1.1814304,1.171762 0 0 0 0.228288,-0.990993 L 0.53894439,7.6775738 A 1.1814304,1.171762 0 0 0 -0.10026101,6.8834313 L -6.2790037,3.9321555 A 1.1814304,1.171762 0 0 0 -6.8523435,3.8176372 Z m 0.00299,0.4550789 a 1.1191255,1.1099671 0 0 1 0.5426517,0.1085303 l 5.85315169,2.7948425 A 1.1191255,1.1099671 0 0 1 0.15197811,7.9290648 L 1.598051,14.21035 a 1.1191255,1.1099671 0 0 1 -0.2163123,0.939348 l -4.0493032,5.037304 a 1.1191255,1.1099671 0 0 1 -0.8749789,0.416906 l -6.4961006,0.0015 a 1.1191255,1.1099671 0 0 1 -0.874979,-0.417652 l -4.0508,-5.036554 a 1.1191255,1.1099671 0 0 1 -0.06362,-0.08832 1.1191255,1.1099671 0 0 1 -0.151942,-0.851028 l 1.443827,-6.2812853 a 1.1191255,1.1099671 0 0 1 0.605524,-0.7529758 l 5.8524036,-2.7963395 a 1.1191255,1.1099671 0 0 1 0.4288819,-0.1085303 z" style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;marker:none;enable-background:accumulate" inkscape:connector-curvature="0" /></g><g id="k8s-c-role-g3341" transform="translate(0,0.28092246)"><ellipse id="k8s-c-role-ellipse7838-2" pointer-events="none" ry="0.50229818" rx="0.50633514" cy="9.9493122" cx="9.9282999" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.12665248" /><path id="k8s-c-role-path7840-40" pointer-events="none" d="m 10.010926,5.8761701 -3.1080511,1.374181 0,2.0505009 c 0,1.901056 1.3247841,3.677988 3.1080511,4.109873 1.780734,-0.431885 3.106786,-2.208817 3.106786,-4.109873 l 0,-2.0505009 z m 1.645216,5.5182459 -3.291698,0 0,-2.7635579 0.472414,0 0,-0.301428 c 0,-0.650995 0.53194,-1.17914 1.189266,-1.17914 0.657327,0 1.189267,0.528145 1.189267,1.17914 l 0,0.301428 0.440751,0 z" inkscape:connector-curvature="0" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.12665248" /><path id="k8s-c-role-path7836-7" pointer-events="none" d="m 9.9878259,7.5890498 c -0.393889,0.0014 -0.713053,0.317896 -0.713053,0.707987 l 0,0.296364 1.4273741,0 0,-0.301428 c -0.0038,-0.388828 -0.321698,-0.701659 -0.7143211,-0.702923 z" inkscape:connector-curvature="0" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.12665248" /></g></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="18.035334mm" height="17.500378mm" viewBox="0 0 18.035334 17.500378" version="1.1" id="k8s-cm-svg13826" inkscape:version="0.91 r13725" sodipodi:docname="cm.svg"><defs id="k8s-cm-defs13820" /><g inkscape:label="Calque 1" inkscape:groupmode="layer" id="k8s-cm-layer1" transform="translate(-0.99262638,-1.174181)"><g id="k8s-cm-g70" transform="matrix(1.0148887,0,0,1.0148887,16.902146,-2.698726)"><path inkscape:export-ydpi="250.55" inkscape:export-xdpi="250.55" inkscape:export-filename="new.png" inkscape:connector-curvature="0" id="k8s-cm-path3055" d="m -6.8492015,4.2724668 a 1.1191255,1.1099671 0 0 0 -0.4288818,0.1085303 l -5.8524037,2.7963394 a 1.1191255,1.1099671 0 0 0 -0.605524,0.7529759 l -1.443828,6.2812846 a 1.1191255,1.1099671 0 0 0 0.151943,0.851028 1.1191255,1.1099671 0 0 0 0.06362,0.08832 l 4.0508,5.036555 a 1.1191255,1.1099671 0 0 0 0.874979,0.417654 l 6.4961011,-0.0015 a 1.1191255,1.1099671 0 0 0 0.8749788,-0.416906 L 1.3818872,15.149453 A 1.1191255,1.1099671 0 0 0 1.5981986,14.210104 L 0.15212657,7.9288154 A 1.1191255,1.1099671 0 0 0 -0.45339794,7.1758396 L -6.3065496,4.3809971 A 1.1191255,1.1099671 0 0 0 -6.8492015,4.2724668 Z" style="fill:#326ce5;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path id="k8s-cm-path3054-2-9" d="M -6.8523435,3.8176372 A 1.1814304,1.171762 0 0 0 -7.3044284,3.932904 l -6.1787426,2.9512758 a 1.1814304,1.171762 0 0 0 -0.639206,0.794891 l -1.523915,6.6308282 a 1.1814304,1.171762 0 0 0 0.160175,0.89893 1.1814304,1.171762 0 0 0 0.06736,0.09281 l 4.276094,5.317236 a 1.1814304,1.171762 0 0 0 0.92363,0.440858 l 6.8576188,-0.0015 a 1.1814304,1.171762 0 0 0 0.9236308,-0.44011 l 4.2745966,-5.317985 a 1.1814304,1.171762 0 0 0 0.228288,-0.990993 L 0.53894439,7.6775738 A 1.1814304,1.171762 0 0 0 -0.10026101,6.8834313 L -6.2790037,3.9321555 A 1.1814304,1.171762 0 0 0 -6.8523435,3.8176372 Z m 0.00299,0.4550789 a 1.1191255,1.1099671 0 0 1 0.5426517,0.1085303 l 5.85315169,2.7948425 A 1.1191255,1.1099671 0 0 1 0.15197811,7.9290648 L 1.598051,14.21035 a 1.1191255,1.1099671 0 0 1 -0.2163123,0.939348 l -4.0493032,5.037304 a 1.1191255,1.1099671 0 0 1 -0.8749789,0.416906 l -6.4961006,0.0015 a 1.1191255,1.1099671 0 0 1 -0.874979,-0.417652 l -4.0508,-5.036554 a 1.1191255,1.1099671 0 0 1 -0.06362,-0.08832 1.1191255,1.1099671 0 0 1 -0.151942,-0.851028 l 1.443827,-6.2812853 a 1.1191255,1.1099671 0 0 1 0.605524,-0.7529758 l 5.8524036,-2.7963395 a 1.1191255,1.1099671 0 0 1 0.4288819,-0.1085303 z" style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;marker:none;enable-background:accumulate" inkscape:connector-curvature="0" /></g><g id="k8s-cm-g3349" transform="translate(0.11778981,0.45794291)"><path inkscape:export-ydpi="376.57999" inkscape:export-xdpi="376.57999" style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" inkscape:connector-curvature="0" d="m 8.236948,6.2914262 5.825001,0" id="k8s-cm-path876" /><path inkscape:export-ydpi="376.57999" inkscape:export-xdpi="376.57999" style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" inkscape:connector-curvature="0" d="m 5.723058,6.2914262 1.45834,0" id="k8s-cm-path880" /><a id="k8s-cm-a3346"><path id="k8s-cm-path884" d="m 10.353619,8.4080928 3.70833,0" inkscape:connector-curvature="0" style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" inkscape:export-xdpi="376.57999" inkscape:export-ydpi="376.57999" /></a><path inkscape:export-ydpi="376.57999" inkscape:export-xdpi="376.57999" style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" inkscape:connector-curvature="0" d="m 7.839728,8.4080928 1.4583305,0" id="k8s-cm-path888" /><path inkscape:export-ydpi="376.57999" inkscape:export-xdpi="376.57999" style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" inkscape:connector-curvature="0" d="m 10.353619,10.52476 3.70833,0" id="k8s-cm-path892" /><path inkscape:export-ydpi="376.57999" inkscape:export-xdpi="376.57999" style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" inkscape:connector-curvature="0" d="m 7.839728,10.52476 1.4583305,0" id="k8s-cm-path896" /><path inkscape:export-ydpi="376.57999" inkscape:export-xdpi="376.57999" style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" inkscape:connector-curvature="0" d="m 8.236948,12.641428 5.825001,0" id="k8s-cm-path900" /><path inkscape:export-ydpi="376.57999" inkscape:export-xdpi="376.57999" style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" inkscape:connector-curvature="0" d="m 5.723058,12.641428 1.45834,0" id="k8s-cm-path904" /></g></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="18.035334mm" height="17.500378mm" viewBox="0 0 18.035334 17.500378" version="1.1" id="k8s-crb-svg13826" inkscape:version="0.91 r13725" sodipodi:docname="crb.svg"><defs id="k8s-crb-defs13820" /><g inkscape:label="Calque 1" inkscape:groupmode="layer" id="k8s-crb-layer1" transform="translate(-0.99262638,-1.174181)"><g id="k8s-crb-g70" transform="matrix(1.0148887,0,0,1.0148887,16.902146,-2.698726)"><path inkscape:export-ydpi="250.55" inkscape:export-xdpi="250.55" inkscape:export-filename="new.png" inkscape:connector-curvature="0" id="k8s-crb-path3055" d="m -6.8492015,4.2724668 a 1.1191255,1.1099671 0 0 0 -0.4288818,0.1085303 l -5.8524037,2.7963394 a 1.1191255,1.1099671 0 0 0 -0.605524,0.7529759 l -1.443828,6.2812846 a 1.1191255,1.1099671 0 0 0 0.151943,0.851028 1.1191255,1.1099671 0 0 0 0.06362,0.08832 l 4.0508,5.036555 a 1.1191255,1.1099671 0 0 0 0.874979,0.417654 l 6.4961011,-0.0015 a 1.1191255,1.1099671 0 0 0 0.8749788,-0.416906 L 1.3818872,15.149453 A 1.1191255,1.1099671 0 0 0 1.5981986,14.210104 L 0.15212657,7.9288154 A 1.1191255,1.1099671 0 0 0 -0.45339794,7.1758396 L -6.3065496,4.3809971 A 1.1191255,1.1099671 0 0 0 -6.8492015,4.2724668 Z" style="fill:#326ce5;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><path id="k8s-crb-path3054-2-9" d="M -6.8523435,3.8176372 A 1.1814304,1.171762 0 0 0 -7.3044284,3.932904 l -6.1787426,2.9512758 a 1.1814304,1.171762 0 0 0 -0.639206,0.794891 l -1.523915,6.6308282 a 1.1814304,1.171762 0 0 0 0.160175,0.89893 1.1814304,1.171762 0 0 0 0.06736,0.09281 l 4.276094,5.317236 a 1.1814304,1.171762 0 0 0 0.92363,0.440858 l 6.8576188,-0.0015 a 1.1814304,1.171762 0 0 0 0.9236308,-0.44011 l 4.2745966,-5.317985 a 1.1814304,1.171762 0 0 0 0.228288,-0.990993 L 0.53894439,7.6775738 A 1.1814304,1.171762 0 0 0 -0.10026101,6.8834313 L -6.2790037,3.9321555 A 1.1814304,1.171762 0 0 0 -6.8523435,3.8176372 Z m 0.00299,0.4550789 a 1.1191255,1.1099671 0 0 1 0.5426517,0.1085303 l 5.85315169,2.7948425 A 1.1191255,1.1099671 0 0 1 0.15197811,7.9290648 L 1.598051,14.21035 a 1.1191255,1.1099671 0 0 1 -0.2163123,0.939348 l -4.0493032,5.037304 a 1.1191255,1.1099671 0 0 1 -0.8749789,0.416906 l -6.4961006,0.0015 a 1.1191255,1.1099671 0 0 1 -0.874979,-0.417652 l -4.0508,-5.036554 a 1.1191255,1.1099671 0 0 1 -0.06362,-0.08832 1.1191255,1.1099671 0 0 1 -0.151942,-0.851028 l 1.443827,-6.2812853 a 1.1191255,1.1099671 0 0 1 0.605524,-0.7529758 l 5.8524036,-2.7963395 a 1.1191255,1.1099671 0 0 1 0.4288819,-0.1085303 z" style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;marker:none;enable-background:accumulate" inkscape:connector-curvature="0" /></g><path id="k8s-crb-path8732" d="m 6.8821333,9.92437 c 0,-0.660388 0.53682,-1.197192 1.1972,-1.197192 l 1.5447698,0 0,-0.733764 -1.5447698,0 c -1.06589,0 -1.93095,0.865069 -1.93095,1.930956 0,1.065887 0.86506,1.930956 1.93095,1.930956 l 1.5447698,0 0,-0.733764 -1.5447698,0 c -0.66038,0 -1.1972,-0.536806 -1.1972,-1.197192 z m 1.5834,0.386191 3.0895197,0 0,-0.772382 -3.0895197,0 z m 3.4757197,-2.317147 -1.54477,0 0,0.733764 1.54477,0 c 0.66038,0 1.19718,0.536804 1.19718,1.197192 0,0.660386 -0.5368,1.197192 -1.19718,1.197192 l -1.54477,0 0,0.733764 1.54477,0 c 1.06589,0 1.93095,-0.865069 1.93095,-1.930956 0,-1.065887 -0.86506,-1.930956 -1.93095,-1.930956 z" inkscape:connector-curvature="0" style="fill:#ffffff;fill-opacity:1;stroke-width:0.3861911" /></g></svg>