From 45342df1bd294016d0bba279073660bd5b398103 Mon Sep 17 00:00:00 2001 From: DeeCee03 Date: Tue, 11 Aug 2026 23:40:48 +0200 Subject: [PATCH] Post 2: Slide 5 und 6 als Handzeichnung Die grauen Kaesten sind raus. Beide Slides tragen jetzt gezeichnete Visualisierungen aus einer kleinen Primitiv-Bibliothek: Strich, Rechteck, Kringel, Wabe, Schraffur, Pfeil, Bodenschatten. Alles Inline-SVG, keine neue Bibliothek, kein Netz. Der Zufallskeim ist fest verdrahtet, und das ist kein Schoenheitsfehler: der Export rendert bei fehlgeschlagener Pruefung bis zu fuenfmal neu. Mit ungesetztem Math.random zittert jeder Durchgang anders, und die Kontrolle vergleicht gegen ein Bild, das es so nie wieder gibt. Slide 5 zeigt HiveGrid als Wabe in der Mitte, sechs Satelliten drumherum. Die Winkel sind nicht frei gewaehlt: eine Flat-Top-Wabe mit Verhaeltnis 2/Wurzel3 ist ein regelmaessiges Sechseck, ihre Ecken liegen also auf 0/60/120/180/240/300 Grad. Jede Speiche setzt dadurch an einer Ecke an statt irgendwo auf einer Kante. Slide 6 uebersetzt die Sektion "So geht's weiter" von der Website: drei Schritte, Pfeile dazwischen, Brief, Sprechblase, Knallbonbon. Die auf der Website gefuellten Sechsecke sind hier schraffiert. In einer Zeichnung ist Schraffur die Fuellung, und der Raster-Grund bleibt dabei sichtbar. Das Layout-CSS beider alter Visualisierungen ist entfernt. Die Klassen haengen weiter am Container, und .vis-verbinden mit seinem display:grid hat das neue SVG erst als Rasterzelle auf ein Drittel zusammengeschoben. Wer eine Visualisierung auf SVG umstellt, muss ihr Layout-CSS mitnehmen. An beiden Stellen steht jetzt ein Warnkommentar. Das SVG traegt width und height als Attribute, nicht nur den viewBox. Ohne sie hat Chrome die Hoehe nicht aus dem Seitenverhaeltnis abgeleitet und die Zeichnung lief in den Fliesstext. Co-Authored-By: Claude Opus 5 (1M context) --- post2.html | 487 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 415 insertions(+), 72 deletions(-) diff --git a/post2.html b/post2.html index c534ba8..32a196d 100644 --- a/post2.html +++ b/post2.html @@ -373,6 +373,8 @@ width: var(--vis-w, 896px); color: var(--head-color); } + .vis svg { display: block; width: 100%; height: auto; } + .vis-kasten { position: relative; padding: 30px 34px; background: var(--frame-bg); @@ -451,59 +453,14 @@ .vis-lose .feld:nth-child(odd) .stummel { right: -84px; } .vis-lose .feld:nth-child(even) .stummel { left: -84px; } - /* Slide 5: das ERP bleibt in der Mitte, das Neue haengt drumherum. */ - .vis-verbinden { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; } - .vis-verbinden .saeule { display: grid; gap: 24px; } - .vis-verbinden .saeule .vis-kasten { min-height: 340px; } - .vis-verbinden .mitte { position: relative; width: 452px; padding: 0 30px; } - .vis-nabe { - position: relative; width: 392px; height: 340px; margin: 0 auto; - background: var(--shape-fill); - clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%); - display: flex; flex-direction: column; align-items: center; justify-content: center; - color: #fff; text-align: center; - } - .vis-nabe b { font-size: 34px; font-weight: 800; letter-spacing: -0.02em; } - .vis-nabe small { margin-top: 6px; font-size: 21px; font-weight: 600; opacity: .74; } - .vis-verbinden .quer { position: absolute; top: 50%; width: 30px; height: 8px; background: var(--shape-line); } - .vis-verbinden .quer.li { left: 0; } - .vis-verbinden .quer.re { right: 0; } + /* Slide 5 ist gezeichnet und braucht kein eigenes Layout mehr: das SVG + fuellt den Container. Das alte Raster stand hier noch und hat die + Zeichnung als Rasterzelle auf ein Drittel zusammengeschoben. Wer eine + Visualisierung auf SVG umstellt, muss ihr Layout-CSS mit entfernen. */ - /* Slide 6: der Abschluss ist ein Feld, kein Bild. */ - .vis-warteliste { - border: 6px solid var(--shape-line); padding: 54px 48px; text-align: center; - clip-path: polygon( - 22px 0, calc(100% - 22px) 0, 100% 22px, 100% calc(100% - 22px), - calc(100% - 22px) 100%, 22px 100%, 0 calc(100% - 22px), 0 22px - ); - } - .vis-warteliste .oben { - font-size: 21px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; - color: var(--body-color); - } - .vis-warteliste .feldzeile { display: flex; align-items: stretch; gap: 18px; margin-top: 26px; } - .vis-warteliste .eingabe { - flex: 1; padding: 30px 32px; text-align: left; - font-size: 30px; font-weight: 500; color: var(--body-color); - background: var(--frame-bg); - clip-path: polygon( - 16px 0, calc(100% - 16px) 0, 100% 16px, 100% calc(100% - 16px), - calc(100% - 16px) 100%, 16px 100%, 0 calc(100% - 16px), 0 16px - ); - } - .vis-warteliste .knopf { - flex: none; display: flex; align-items: center; padding: 30px 40px; - font-size: 30px; font-weight: 800; - background: var(--accent-yellow); color: #3E2C0A; - clip-path: polygon( - 16px 0, calc(100% - 16px) 0, 100% 16px, 100% calc(100% - 16px), - calc(100% - 16px) 100%, 16px 100%, 0 calc(100% - 16px), 0 16px - ); - } - .vis-warteliste .adresse { - margin-top: 30px; font-size: 30px; font-weight: 800; letter-spacing: -0.01em; - color: var(--head-color); - } + /* Slide 6 ist gezeichnet, das alte Formular-Layout ist entfallen. Die + Klasse .vis-warteliste haengt weiter am Container: Layout-CSS darf hier + nicht wieder auftauchen, sonst rahmt es das SVG ein. */ /* ─── Teamkarte ────────────────────────────────────────────────────────── Nachbau der Karte von der Ueber-uns-Seite im aktiven Zustand: Hintergrund @@ -1251,7 +1208,7 @@ eyebrow: "Warteliste", head: 'Gebaut im Betrieb.
Für den Betrieb.', body: "Setz dich auf die Warteliste.", - vis: "warteliste", visW: 820, visLeft: 130, visBottom: 281, + vis: "warteliste", visW: 896, visLeft: 92, visBottom: 120, }, ]; @@ -1299,6 +1256,409 @@ (zeile ? '
' + zeile + '
' : '') + ''; + /* ─── Skizzen-Primitive ───────────────────────────────────────────────────── + Handzeichnung aus gesetztem Zufall. Der Keim ist fest: der Export rendert + bei fehlgeschlagener Pruefung bis zu fuenfmal neu, und jeder Durchgang muss + denselben Strich ergeben. Mit ungesetztem Math.random zittert jedes Bild + anders und die Kontrolle vergleicht gegen etwas, das es nie wieder gibt. */ + let _s = 0; + function keim(n) { _s = n >>> 0; } + function rnd() { + _s = (_s + 0x6D2B79F5) >>> 0; + let t = Math.imul(_s ^ (_s >>> 15), 1 | _s); + t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; + return ((t ^ (t >>> 14)) >>> 0) / 4294967296; + } + const zit = m => (rnd() * 2 - 1) * m; + const n1 = v => Math.round(v * 10) / 10; + + /* Ein Strich, leicht gebogen und an den Enden verwackelt. Der Bogen waechst + mit der Laenge: eine kurze Kante zittert, eine lange schwingt. */ + function strich(x1, y1, x2, y2, o) { + o = o || {}; + const j = o.j == null ? 1.5 : o.j; + const ue = o.ue == null ? 0 : o.ue; // Ueberschuss an den Enden + let dx = x2 - x1, dy = y2 - y1; + const len = Math.hypot(dx, dy) || 1, ux = dx / len, uy = dy / len; + const ax = x1 - ux * rnd() * ue + zit(j), ay = y1 - uy * rnd() * ue + zit(j); + const bx = x2 + ux * rnd() * ue + zit(j), by = y2 + uy * rnd() * ue + zit(j); + dx = bx - ax; dy = by - ay; + const nx = -dy / len, ny = dx / len; + const b = o.b == null ? len * 0.008 + 1 : o.b; + const o1 = zit(b), o2 = zit(b); + return "M" + n1(ax) + "," + n1(ay) + "C" + + n1(ax + dx / 3 + nx * o1) + "," + n1(ay + dy / 3 + ny * o1) + " " + + n1(ax + dx * 2 / 3 + nx * o2) + "," + n1(ay + dy * 2 / 3 + ny * o2) + " " + + n1(bx) + "," + n1(by); + } + + /* Punktfolge zu einer weichen Kurve (Catmull-Rom als Bezier). */ + function glatt(pts, zu) { + const n = pts.length; + const at = i => pts[zu ? (i + n) % n : Math.max(0, Math.min(n - 1, i))]; + let d = "M" + n1(pts[0][0]) + "," + n1(pts[0][1]); + const bis = zu ? n : n - 1; + for (let i = 0; i < bis; i++) { + const p0 = at(i - 1), p1 = at(i), p2 = at(i + 1), p3 = at(i + 2); + d += "C" + n1(p1[0] + (p2[0] - p0[0]) / 6) + "," + n1(p1[1] + (p2[1] - p0[1]) / 6) + + " " + n1(p2[0] - (p3[0] - p1[0]) / 6) + "," + n1(p2[1] - (p3[1] - p1[1]) / 6) + + " " + n1(p2[0]) + "," + n1(p2[1]); + } + return d; + } + + /* Kringel statt Kreis: der Zug faengt irgendwo an, laeuft einmal herum und + schiesst ein Stueck ueber den Anfang hinaus. Genau das macht die Hand. */ + function kringel(cx, cy, rx, ry, o) { + o = o || {}; + ry = ry == null ? rx : ry; + const wack = o.wack == null ? Math.min(rx, ry) * 0.02 + 1 : o.wack; + const a0 = o.a0 == null ? rnd() * 6.283 : o.a0; + const ueber = o.ueber == null ? 0.16 + rnd() * 0.2 : o.ueber; + const schritte = 18, pts = []; + const spanne = 6.283 + ueber; + for (let i = 0; i <= schritte; i++) { + const a = a0 + spanne * (i / schritte); + pts.push([cx + Math.cos(a) * (rx + zit(wack)), cy + Math.sin(a) * (ry + zit(wack))]); + } + return glatt(pts, false); + } + + /* Vier einzelne Kanten mit Ueberschuss an den Ecken. Der Ueberschuss ist das + eigentliche Erkennungsmerkmal von Handzeichnung, nicht das Zittern. */ + function rechteck(x, y, w, h, o) { + o = o || {}; + const ue = o.ue == null ? 3.5 : o.ue; + return [ + strich(x, y, x + w, y, { ue: ue }), + strich(x + w, y, x + w, y + h, { ue: ue }), + strich(x + w, y + h, x, y + h, { ue: ue }), + strich(x, y + h, x, y, { ue: ue }), + ].join(""); + } + + /* Schraffur als Schattierung: parallele Striche, an den Enden kuerzer, damit + das Feld nicht als Block steht. */ + function schraff(x, y, w, h, abstand, neig) { + neig = neig == null ? 0.55 : neig; + let d = ""; + for (let t = -h * neig; t < w; t += abstand) { + const x1 = x + t, x2 = x + t + h * neig; + const a = Math.max(x, Math.min(x + w, x1)), b = Math.max(x, Math.min(x + w, x2)); + if (b - a < 3) continue; + const y1 = y + h - (a - x1) / neig, y2 = y + h - (b - x1) / neig; + d += strich(a, Math.min(y + h, Math.max(y, y1)), b, Math.min(y + h, Math.max(y, y2)), { j: .8, b: .8 }); + } + return d; + } + + /* Bodenschatten: ein paar kurze Striche unter dem Gegenstand. Sie sitzen eng + am Fuss, sonst schwebt der Gegenstand ueber seinem eigenen Schatten. */ + function boden(cx, y, w) { + let d = ""; + for (let i = 0; i < 4; i++) { + const f = 1 - Math.abs(i - 1.5) * 0.26; + const b = w * f * (0.62 + rnd() * 0.26); + d += strich(cx - b / 2 + zit(3), y + i * 3.6, cx + b / 2 + zit(3), y + i * 3.6 - 4, { j: .9, b: 1.2 }); + } + return d; + } + + /* ─── Bausteine ────────────────────────────────────────────────────────── */ + const TINTE = "#2A2C33", GELB = "#E9A81C"; + const zug = (d, o) => { + o = o || {}; + return ''; + }; + const schatten = d => zug(d, { w: 1.9, op: .34 }); + + /* ─── Die Satelliten ───────────────────────────────────────────────────── + Jedes Ikon meldet zurueck, wie weit es reicht (rad) und wo seine + Beschriftung hingehoert (unten). Sonst haengt der Knoten am kleinen Ikon + zu weit weg und am grossen klebt er drin. */ + function ikonKi(cx, cy) { + const zack = (x, y, r) => { + const k = r * 0.28, p = []; + for (let i = 0; i < 8; i++) { + const a = -Math.PI / 2 + i * Math.PI / 4, rr = i % 2 ? k : r; + p.push([x + Math.cos(a) * rr + zit(1.4), y + Math.sin(a) * rr + zit(1.4)]); + } + return glatt(p, true); + }; + return zug(zack(cx - 8, cy - 6, 46), { w: 3.4 }) + + zug(zack(cx + 38, cy + 30, 17), { w: 2.7 }) + + zug(zack(cx + 34, cy - 34, 12), { w: 2.5 }) + + schatten(boden(cx, cy + 54, 88)); + } + ikonKi.rad = 52; ikonKi.unten = 92; + + function ikonChat(cx, cy) { + const blase = (x, y, rx, ry, sx) => + zug(kringel(x, y, rx, ry, { ueber: .12 })) + + zug(glatt([[x + sx * rx * .42, y + ry * .82], [x + sx * rx * .86, y + ry * 1.6], + [x + sx * rx * .08, y + ry * .98]], false)); + return blase(cx + 20, cy - 4, 38, 30, 1) + + blase(cx - 22, cy - 16, 47, 37, -1) + + [-18, 0, 18].map(o => zug(kringel(cx - 22 + o, cy - 16, 4.6), { w: 2.5 })).join("") + + schatten(boden(cx, cy + 52, 100)); + } + ikonChat.rad = 58; ikonChat.unten = 90; + + function ikonCrm(cx, cy) { + const kopf = (x, y, r, w) => zug(kringel(x, y, r), { w: w }) + + zug(glatt([[x - r * 1.55, y + r * 2.7], [x - r * 1.5, y + r * 1.05], + [x, y + r * .55], [x + r * 1.5, y + r * 1.05], [x + r * 1.55, y + r * 2.7]], false), { w: w }); + return kopf(cx - 42, cy - 16, 17, 2.8) + kopf(cx + 42, cy - 16, 17, 2.8) + + kopf(cx, cy + 2, 22, 3.3) + + schatten(boden(cx, cy + 62, 116)); + } + ikonCrm.rad = 72; ikonCrm.unten = 92; + + function ikonAuto(cx, cy) { + const r = 39, zaehne = 10; + let d = ""; + for (let i = 0; i < zaehne; i++) { + const a = i * 6.283 / zaehne, br = 0.22; + const p = []; + [[-br, r], [-br * .8, r + 14], [br * .8, r + 14], [br, r]].forEach(v => { + const aa = a + v[0]; + p.push([cx + Math.cos(aa) * v[1] + zit(.9), cy + Math.sin(aa) * v[1] + zit(.9)]); + }); + d += glatt(p, false); + } + return zug(kringel(cx, cy, r)) + zug(d, { w: 2.9 }) + zug(kringel(cx, cy, 15), { w: 2.7 }) + + schatten(boden(cx, cy + 60, 96)); + } + ikonAuto.rad = 56; ikonAuto.unten = 88; + + function ikonBoard(cx, cy) { + const w = 116, h = 88, x = cx - w / 2, y = cy - h / 2; + let d = rechteck(x, y, w, h); + let k = ""; + for (let i = 1; i < 3; i++) k += strich(x + w * i / 3, y + 7, x + w * i / 3, y + h - 7, { j: 1, b: 1 }); + const karte = (sx, sy, sh) => rechteck(x + sx, y + sy, w / 3 - 16, sh, { ue: 2 }); + k += karte(8, 11, 19) + karte(8, 36, 19) + karte(w / 3 + 8, 11, 19) + karte(2 * w / 3 + 8, 11, 31); + return zug(d) + zug(k, { w: 2.4 }) + schatten(boden(cx, cy + h / 2 + 7, 116)); + } + ikonBoard.rad = 64; ikonBoard.unten = 84; + + function ikonFormular(cx, cy) { + const w = 90, h = 116, x = cx - w / 2, y = cy - h / 2; + let d = rechteck(x, y, w, h); + d += rechteck(x + w / 2 - 19, y - 12, 38, 22, { ue: 2 }); + let k = ""; + [32, 50, 68].forEach(o => { k += strich(x + 15, y + o, x + w - 34, y + o, { j: 1, b: 1 }); }); + k += rechteck(x + 15, y + 80, 23, 23, { ue: 2 }); + const hak = [[x + 19, y + 91], [x + 25, y + 98], [x + 35, y + 81]]; + return zug(d) + zug(k, { w: 2.4 }) + zug(glatt(hak, false), { w: 3.6, f: GELB }) + + schatten(boden(cx, cy + h / 2 + 7, 92)); + } + ikonFormular.rad = 62; ikonFormular.unten = 96; + + /* Ein Strich mit Spitze am Ende. Die Spitze sind zwei kurze Zuege, keine + gefuellte Flaeche: gezeichnet wird sie so, gefuellt sieht sie gesetzt aus. */ + function pfeil(x1, y1, x2, y2, gr) { + gr = gr == null ? 16 : gr; + const a = Math.atan2(y2 - y1, x2 - x1), oeff = 0.42; + return strich(x1, y1, x2, y2, { j: 1.1 }) + + strich(x2, y2, x2 - Math.cos(a - oeff) * gr, y2 - Math.sin(a - oeff) * gr, { j: .8, b: .8 }) + + strich(x2, y2, x2 - Math.cos(a + oeff) * gr, y2 - Math.sin(a + oeff) * gr, { j: .8, b: .8 }); + } + + /* Schraffur innerhalb einer Wabe. Eine gezeichnete Flaeche wird nicht + gefuellt, sie wird schraffiert: so bleibt der Raster-Grund sichtbar und + es sieht trotzdem nach "voll" aus wie auf der Website. */ + function wabeSchraff(cx, cy, r, abstand) { + const h = r * 0.866; + let d = ""; + for (let y = -h + abstand; y < h - abstand * 0.5; y += abstand) { + const halb = r - (r / 2) * (Math.abs(y) / h) - 8; + if (halb < 7) continue; + d += strich(cx - halb, cy + y - 3, cx + halb, cy + y + 3, { j: .9, b: 1 }); + } + return d; + } + + function ikonBrief(cx, cy, f) { + const w = 46, h = 31, x = cx - w / 2, y = cy - h / 2; + return zug(rechteck(x, y, w, h, { ue: 2.5 }), { w: 2.9, f: f }) + + zug(glatt([[x + 3, y + 4], [cx, y + h * 0.66], [x + w - 3, y + 4]], false), + { w: 2.7, f: f }); + } + + function ikonBlaseKlein(cx, cy, f) { + return zug(kringel(cx, cy - 5, 25, 19, { ueber: .1 }), { w: 2.9, f: f }) + + zug(glatt([[cx - 8, cy + 12], [cx - 13, cy + 23], [cx + 3, cy + 13]], false), + { w: 2.7, f: f }) + + [-9, 0, 9].map(o => zug(kringel(cx + o, cy - 5, 2.8), { w: 2.2, f: f })).join(""); + } + + function ikonKonfetti(cx, cy, f) { + // Knallbonbon: Tuete unten links, Funken nach oben rechts. + const tuete = glatt([[cx - 28, cy + 24], [cx - 6, cy - 2], + [cx + 10, cy + 19], [cx - 28, cy + 24]], false) + + strich(cx - 20, cy + 15, cx - 2, cy + 19, { j: .9, b: .8 }); + let funken = ""; + [-1.42, -1.06, -0.68, -0.28].forEach((a, i) => { + const r0 = 16, r1 = 30 + (i % 2) * 7; + funken += strich(cx + 2 + Math.cos(a) * r0, cy + 3 + Math.sin(a) * r0, + cx + 2 + Math.cos(a) * r1, cy + 3 + Math.sin(a) * r1, { j: 1 }); + }); + let punkte = ""; + [[20, -27], [31, -11], [26, -35], [8, -33]].forEach(v => { + punkte += kringel(cx + v[0], cy + v[1], 3.1); + }); + return zug(tuete, { w: 2.9, f: f }) + zug(funken, { w: 2.4, f: f }) + + zug(punkte, { w: 2.2, f: f }); + } + + /* Die Wabe, flat-top, Seitenverhaeltnis 2/Wurzel3 wie in rules.md Paragraf 3. + Bei dieser Form liegen die sechs Ecken auf einem Kreis vom Radius r. Genau + deshalb ist sie hier richtig: jede Speiche setzt an einer Ecke an, nicht + irgendwo auf einer Kante. Gezogen wird sie aus sechs einzelnen Strichen + mit Ueberschuss, nicht als geschlossenes Polygon. */ + function wabeEcken(cx, cy, r) { + const p = []; + for (let i = 0; i < 6; i++) { + const a = i * Math.PI / 3; + p.push([cx + Math.cos(a) * r, cy + Math.sin(a) * r]); + } + return p; + } + + function wabe(cx, cy, r) { + const p = wabeEcken(cx, cy, r); + let d = ""; + for (let i = 0; i < 6; i++) { + const q = p[i], w = p[(i + 1) % 6]; + d += strich(q[0], q[1], w[0], w[1], { ue: 5, j: 1.8 }); + } + return d; + } + + /* ─── Die Mitte: das bestehende System ─────────────────────────────────── + Bewusst als Liste gezeichnet, nicht als Kachel-Dashboard: so sieht eine + Warenwirtschaft aus. Und bewusst der schwerste Strich auf der Slide, denn + sie ist das, was bleibt. Das Fenster passt ganz in den Ring, sonst kreuzen + sich Ringbogen und Fensterecke, und weil der Grund durchscheinen soll gibt + es keine Fuellung, die das verdecken koennte. */ + function nabe(cx, cy) { + const w = 216, h = 150, x = cx - w / 2, y = cy - h / 2, kopf = 40; + let d = rechteck(x, y, w, h, { ue: 4 }); + d += strich(x, y + kopf, x + w, y + kopf, { ue: 3 }); + let k = ""; + [0, 1, 2].forEach(i => { k += kringel(x + 18 + i * 15, y + kopf / 2, 4.4, 4.4, { ueber: .1 }); }); + let z = ""; + for (let i = 0; i < 4; i++) { + const yy = y + kopf + 22 + i * 23; + z += strich(x + 17, yy, x + 59, yy, { j: 1, b: 1 }); + z += strich(x + 72, yy, x + w - 48, yy, { j: 1, b: 1 }); + z += strich(x + w - 38, yy, x + w - 17, yy, { j: 1, b: 1 }); + } + return zug(d, { w: 3.6 }) + zug(k, { w: 2.4 }) + zug(z, { w: 2.2, op: .8 }) + + 'HiveGrid'; + } + + /* ─── Aufbau ───────────────────────────────────────────────────────────── */ + /* ─── Slide 6: was nach der Anmeldung passiert ─────────────────────────── + Uebernahme der Sektion "So geht\'s weiter" von der Website, in derselben + Handschrift wie Slide 5. Drei Schritte an einer Leitung, Wabe statt Kreis, + der dritte Schritt blass: er hat noch nicht stattgefunden. */ + function visWarteliste() { + keim(20260814); + const W = 896, H = 730, HX = 66, R = 54, TX = 176, HOEH = R * 0.866; + + const schritte = [ + { y: 112, titel: "Du trägst dich ein", ikon: ikonBrief, + zeilen: ["Eine E-Mail genügt. Kein Vertrag,", "keine Kreditkarte, unverbindlich."] }, + { y: 352, titel: "Wir melden uns", ikon: ikonBlaseKlein, + zeilen: ["Sobald HiveGrid für deinen Betrieb", "bereit ist, persönlich und ohne Spam."] }, + { y: 592, titel: "Du bist von Anfang an dabei", ikon: ikonKonfetti, + zeilen: ["Unter den Ersten, die HiveGrid", "nutzen und mitgestalten."] }, + ]; + + const txt = (x, y, groesse, gewicht, farbe, sperr, inhalt) => + '' + inhalt + ''; + + let raus = ""; + + // Von Wabe zu Wabe ein Pfeil. Er endet kurz vor der naechsten Wabe, sonst + // laeuft die Spitze in die Kante und ist nicht mehr als Spitze zu lesen. + let pfeile = ""; + for (let i = 0; i < schritte.length - 1; i++) { + pfeile += pfeil(HX, schritte[i].y + HOEH + 8, HX, schritte[i + 1].y - HOEH - 9); + } + raus += zug(pfeile, { f: GELB, w: 3.8 }); + + schritte.forEach(sc => { + raus += zug(wabeSchraff(HX, sc.y, R, 11), { f: GELB, w: 2, op: .5 }); + raus += zug(wabe(HX, sc.y, R), { f: GELB, w: 4 }); + raus += sc.ikon(HX, sc.y, TINTE); + raus += txt(TX, sc.y - 26, 40, 800, TINTE, "-0.02em", sc.titel); + sc.zeilen.forEach((z, k) => { + raus += txt(TX, sc.y + 24 + k * 34, 26, 500, "#555555", "0", z); + }); + }); + + return '' + raus + ''; + } + + function visVerbinden() { + keim(20260812); + const W = 896, H = 730, cx = 448, cy = 350, R = 160; + const RX = 352, RY = 300; + + // Die Winkel sind nicht frei gewaehlt: sie sind die sechs Eckwinkel der + // Wabe. Dadurch sitzt jeder Knoten auf einer Ecke. + const sats = [ + { a: 240, name: "KI", ikon: ikonKi }, + { a: 300, name: "Chat", ikon: ikonChat }, + { a: 0, name: "Mitarbeiter", ikon: ikonCrm }, + { a: 60, name: "Automation", ikon: ikonAuto }, + { a: 120, name: "Projekte", ikon: ikonBoard }, + { a: 180, name: "Formulare", ikon: ikonFormular }, + ]; + + let speichen = "", knoten = "", bilder = "", schrift = ""; + + sats.forEach(s => { + const r = s.a * Math.PI / 180; + const sx = cx + Math.cos(r) * RX, sy = cy + Math.sin(r) * RY; + const ix = cx + Math.cos(r) * R, iy = cy + Math.sin(r) * R; // Ecke der Wabe + const vx = sx - ix, vy = sy - iy, vl = Math.hypot(vx, vy) || 1; + const ux = vx / vl, uy = vy / vl; + const ax = sx - ux * (s.ikon.rad + 20), ay = sy - uy * (s.ikon.rad + 20); + speichen += strich(ix + ux * 12, iy + uy * 12, ax - ux * 11, ay - uy * 11, { j: 1.2 }); + knoten += kringel(ix, iy, 10.5, 10.5, { ueber: .1 }) + kringel(ax, ay, 10.5, 10.5, { ueber: .1 }); + bilder += s.ikon(sx, sy); + schrift += '' + s.name + ''; + }); + + // Die Wabe wird zweimal gezogen. Ein zweiter Umriss auf groesserem Radius + // sah nach Versehen aus, derselbe Radius nach zweitem Zug. + // Breite und Hoehe stehen als Attribute mit dran, nicht nur im viewBox. + // Ohne sie hat Chrome die Hoehe nicht aus dem Seitenverhaeltnis abgeleitet, + // das SVG wurde zu hoch und die Zeichnung lief in den Fliesstext. + return '' + + bilder + schrift + + zug(speichen, { f: GELB, w: 4.4 }) + + zug(wabe(cx, cy, R), { f: GELB, w: 4.8 }) + + zug(wabe(cx, cy, R - 2), { f: GELB, w: 3.2, op: .72 }) + + zug(knoten, { f: GELB, w: 3.8 }) + + nabe(cx, cy) + + ''; + } + const VIS = { karte: () => '
' + @@ -1338,26 +1698,9 @@ '
' + kasten("ERP", "läuft, redet aber mit niemandem") + '
' + '
', - verbinden: () => - '
' + - '
' + kasten("KI") + kasten("CRM") + '
' + - '
' + - '' + - '
Dein ERPbleibt, wo es ist
' + - '' + - '
' + - '
' + kasten("Automation") + kasten("Chat") + '
' + - '
', + verbinden: () => visVerbinden(), - warteliste: () => - '
' + - '
Frühzugang
' + - '
' + - '
deine@firma.de
' + - '
Auf die Warteliste
' + - '
' + - '
hivegrid.de
' + - '
', + warteliste: () => visWarteliste(), }; /* ─── Das Deck ────────────────────────────────────────────────────────────