Theme modern: Slide 5 und 6 aus den echten Quellen gebaut
Bisher habe ich die Skizzen entschraffiert statt neu gedacht. Das ist bei Slide 6 offensichtlich geworden: eine Wabe ohne Zittern ist immer noch meine Wabe und nicht die Sektion, die es auf der Website schon gibt. Slide 6 ist jetzt ein Nachbau von app/waitlist/WaitlistNext.tsx. Alle Werte von dort, mit 1,6 hochskaliert, weil die Liste dort 560 breit ist und hier 896: Wabe 54x48 wird 86x77, Schiene links 26 wird 43 also die Wabenmitte, Abstand 56 wird 90, Nummer 13 wird 21, Titel 20 wird 32, Text 14,5 wird 23. Auf der Website leuchten die Waben beim Scrollen nacheinander auf und der dritte ist blass; ein Standbild kennt keinen Scrollstand, hier sind alle drei aktiv. Slide 5 ist ein Nachbau des Architektur-Diagramms aus dem Pitch-Paket: Kern als gefuellter Kreis in der Mitte, farbiger Ring darum, Pille mit dem Produktnamen auf dem Ring, Module als Kacheln aussen. Das Diagramm sagt genau, was die Ueberschrift sagt -- der Kern bleibt, der Ring kommt drumherum. Meine Wabe hatte dieselbe Aussage, aber in einer Form, die es in unserer Software nirgends gibt. Zwei eigene Fehler dabei behoben: Ich hatte fuer das Theme Farben erfunden (#0E1F1E, #F7FAF9). Die Website hat ein vollstaendiges Token-Set in app/globals.css. Jetzt stehen dort dessen Werte: surface, text-primary, text-secondary, container-bg, border-neutral, accent-yellow-light, accent-yellow-dark, accent-teal-light. Meine Karten hatten Chamfer und Haarlinie zugleich. In lib/chamfer.ts der Website steht ausdruecklich, dass clip-path den Rand mitbeschneidet und man stattdessen fuellen soll. Die Karten sind jetzt gefuellt, ohne Rand. Das Chamfer-Badge der Website ist zurueck. Es steht dort ueber jeder Sektion, und im Theme modern traegt es die Einordnung: Die Geschichte, Der Alltag, Der Wechsel, Was gefehlt hat, Die Loesung, So geht's weiter. Neu im Vorgehen: nach jeder groesseren Aenderung wird das Skript aus der Datei geschnitten und mit node --check geprueft. Ein doppelt vergebener Name hat die Seite still lahmgelegt, und die Pruefung findet so etwas in einer Sekunde statt in einem Renderlauf. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
993dc3e5ef
commit
9b405ce931
2 files changed
+213
-120
No files matched your search
+212
-119
@@ -486,9 +486,9 @@
|
||||
}
|
||||
.page { position: absolute; top: 0; width: 1080px; height: 1350px; }
|
||||
|
||||
/* 59 px oben: das ist die gemessene Oberkante der Ueberschrift auf der
|
||||
Titel-Slide von Post 1. Seitlich bleiben es 92. */
|
||||
.content { position: relative; z-index: 2; padding: 59px 92px 0; }
|
||||
/* Im Theme modern traegt das Badge wieder Platz, deshalb beginnt der Block
|
||||
bei 92 -- dem Rand der Safe Zone -- statt bei den 59 des Themes notebook. */
|
||||
.content { position: relative; z-index: 2; padding: 92px 92px 0; }
|
||||
|
||||
/* Section badge, straight from the site: .chamfer-badge in globals.css with
|
||||
accent-yellow-light on accent-yellow-dark. Scaled from the 12px web size to
|
||||
@@ -499,13 +499,16 @@
|
||||
dadurch 125 px tiefer als die Titel-Slide von Post 1. Jetzt gibt das Tag
|
||||
den Platz frei, und der obere Abstand steht direkt im padding von
|
||||
.content. Wer das Tag je sichtbar macht, muss das padding mitziehen. */
|
||||
/* Das Chamfer-Badge der Website: gelb aufgehellte Flaeche, dunkler Text,
|
||||
Versalien und weite Sperrung. Es steht dort ueber jeder Sektion. Im Theme
|
||||
notebook ist es ausgeblendet, hier traegt es die Einordnung. */
|
||||
.badge {
|
||||
display: inline-block; margin-bottom: 38px;
|
||||
font-size: 26px; font-weight: 600; letter-spacing: 0.1em;
|
||||
padding: 9px 20px;
|
||||
display: inline-block; margin-bottom: 28px;
|
||||
font-size: 24px; font-weight: 600; letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
padding: 11px 22px;
|
||||
background: var(--accent-yellow-light);
|
||||
color: var(--accent-yellow-dark);
|
||||
display: none;
|
||||
clip-path: polygon(
|
||||
11px 0, calc(100% - 11px) 0, 100% 11px, 100% calc(100% - 11px),
|
||||
calc(100% - 11px) 100%, 11px 100%, 0 calc(100% - 11px), 0 11px
|
||||
@@ -521,8 +524,8 @@
|
||||
/* Groesser und enger als im Theme notebook: die Vorbilder setzen die
|
||||
Ueberschrift als Blickfang ueber fast die ganze Breite. */
|
||||
.headline {
|
||||
font-size: 84px; font-weight: 800;
|
||||
letter-spacing: -0.035em; line-height: 1.02;
|
||||
font-size: 76px; font-weight: 800;
|
||||
letter-spacing: -0.032em; line-height: 1.04;
|
||||
color: var(--head-color);
|
||||
}
|
||||
/* Kein Marker. Der Akzent ist im Theme modern ein farbiges Wort -- so
|
||||
@@ -807,9 +810,9 @@
|
||||
|
||||
|
||||
/* ─── Variant: dunkel ────────────────────────────────────────────────── */
|
||||
/* Theme modern, dunkle Variante. Derselbe Aufbau, invertiert. */
|
||||
/* Theme modern, dunkle Variante. bg-dark aus der Website. */
|
||||
.slide[data-variant="dunkel"] {
|
||||
background: #0E1F1E;
|
||||
background: #0F1A2E;
|
||||
--head-color: var(--text-white);
|
||||
--body-color: #B9BDC9;
|
||||
--accent-on: var(--accent-yellow);
|
||||
@@ -826,19 +829,19 @@
|
||||
}
|
||||
|
||||
/* ─── Variant: hell ──────────────────────────────────────────────────── */
|
||||
/* Theme modern, helle Variante. Drei Marken-Farben und sonst nichts:
|
||||
Weiss als Grund, Orange als Akzent, Teal als zweiter Akzent. Die
|
||||
Schriftfarbe ist ein sehr dunkles Teal statt reinem Schwarz, damit sie
|
||||
zur Palette gehoert und nicht danebensteht. */
|
||||
/* Theme modern, helle Variante. Die Werte stammen aus app/globals.css der
|
||||
Website, nicht aus eigener Erfindung: surface, text-primary,
|
||||
text-secondary, container-bg, border-neutral. Eine erfundene Palette war
|
||||
der erste Fehler dieser Fassung. */
|
||||
.slide[data-variant="hell"] {
|
||||
background: #F7FAF9;
|
||||
--head-color: #0E1F1E;
|
||||
--body-color: #5B6D6B;
|
||||
background: #FFFFFF;
|
||||
--head-color: #1A1A1A;
|
||||
--body-color: #555555;
|
||||
--accent-on: #C8901A;
|
||||
--marker: var(--accent-yellow);
|
||||
--frame-bg: #EAF0EF;
|
||||
--shape-fill: #0E1F1E;
|
||||
--shape-line: rgba(14,31,30,.13);
|
||||
--frame-bg: #F6F8FA;
|
||||
--shape-fill: #1A1A1A;
|
||||
--shape-line: #E5E5E5;
|
||||
--pat-color: #30313D; --pat-strength: 0;
|
||||
--gmark-color: rgba(48,49,61,.16);
|
||||
--guide-line: rgba(48,49,61,.20);
|
||||
@@ -1147,7 +1150,7 @@
|
||||
key: "titel", type: "feature",
|
||||
layouts: [{ id: "ecke", label: "Helm auf der Ecke" },
|
||||
{ id: "kopf", label: "Helm auf dem Kopf" }],
|
||||
eyebrow: "Titel",
|
||||
eyebrow: "Die Geschichte",
|
||||
head: 'Die Geschichte fängt<br><span class="a">auf der Baustelle an.</span>',
|
||||
body: "Noah Geerkens, SHK-Betrieb in vierter Generation.",
|
||||
vis: "karte", visW: 620, visLeft: 230, visBottom: 76,
|
||||
@@ -1155,7 +1158,7 @@
|
||||
{
|
||||
key: "prozesse", type: "feature",
|
||||
layouts: [{ id: "standard", label: "Standard" }],
|
||||
eyebrow: "Zettelwirtschaft",
|
||||
eyebrow: "Der Alltag",
|
||||
head: 'Wer lange dabei ist,<br><span class="a">hat nicht nur Erfahrung.</span>',
|
||||
body: "Auch Abläufe, die laufen, weil die Software es vorgibt. Nicht, weil ein Konzept dahintersteckt.",
|
||||
vis: "schiene", visW: 896, visLeft: 92, visBottom: 120,
|
||||
@@ -1163,7 +1166,7 @@
|
||||
{
|
||||
key: "wechseln", type: "feature",
|
||||
layouts: [{ id: "standard", label: "Standard" }],
|
||||
eyebrow: "Wechseln",
|
||||
eyebrow: "Der Wechsel",
|
||||
head: 'Und wechseln?<br><span class="a">Kannst du vergessen.</span>',
|
||||
body: "Da hängen Jahre an Stammdaten, Aufträgen und Buchhaltung drin. Ein Wechsel führt ins Chaos.",
|
||||
vis: "ersetzen", visW: 896, visLeft: 92, visBottom: 190,
|
||||
@@ -1171,7 +1174,7 @@
|
||||
{
|
||||
key: "mehr", type: "feature",
|
||||
layouts: [{ id: "standard", label: "Standard" }],
|
||||
eyebrow: "Mehr",
|
||||
eyebrow: "Was gefehlt hat",
|
||||
head: 'Wir wollten nicht weg.<br><span class="a">Wir wollten mehr.</span>',
|
||||
body: "Ein CRM, Vertrieb, Marketing, Automatisierungen. Unsere Tools sollen endlich miteinander reden.",
|
||||
vis: "lose", visW: 988, visLeft: 92, visBottom: 120,
|
||||
@@ -1179,7 +1182,7 @@
|
||||
{
|
||||
key: "verbinden", type: "feature",
|
||||
layouts: [{ id: "standard", label: "Standard" }],
|
||||
eyebrow: "Verbinden",
|
||||
eyebrow: "Die Lösung",
|
||||
head: 'Dein Branchenprogramm<br><span class="a">bleibt, wo es ist.</span>',
|
||||
body: "Kein Ersatz, sondern der Aufbau drumherum. HiveGrid holt raus, was bisher gefehlt hat.",
|
||||
vis: "verbinden", visW: 988, visLeft: 0, visBottom: 120,
|
||||
@@ -1187,14 +1190,14 @@
|
||||
{
|
||||
key: "warteliste", type: "feature",
|
||||
layouts: [{ id: "standard", label: "Standard" }],
|
||||
eyebrow: "Warteliste",
|
||||
eyebrow: "So geht's weiter",
|
||||
/* Die Marken-Tagline. Sie wird genauso ausgezeichnet wie in Post 1 und
|
||||
im Titel-Layout: der Marker sitzt auf dem zweiten "Handwerker", nicht
|
||||
auf der ganzen Zeile. Eine zweite Schreibweise derselben Zeile waere
|
||||
eine Abweichung, die niemandem auffaellt und trotzdem stoert. */
|
||||
head: 'Vom Handwerker<br>für <span class="a">Handwerker</span>',
|
||||
body: "Kein Startup-Projekt. Setz dich auf die Warteliste, dann melden wir uns.",
|
||||
vis: "warteliste", visW: 896, visLeft: 92, visBottom: 120,
|
||||
vis: "warteliste", visW: 896, visLeft: 92, visBottom: 170,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1311,17 +1314,29 @@
|
||||
Gelb darf dort der volle Markenton sein statt des abgedunkelten. */
|
||||
/* Drei Marken-Farben, dazu Tinte und eine Haarlinie. Teal war in rules.md
|
||||
bisher als "ungenutzt" vermerkt und traegt hier den zweiten Akzent. */
|
||||
let TINTE = "#0E1F1E", GELB = "#F1B729", TEAL = "#0D6E6E",
|
||||
TEXT2 = "#5B6D6B", LINIE = "#DCE4E3", KARTE = "#FFFFFF";
|
||||
/* Alle Werte aus app/globals.css der Website. GELB_DUNKEL ist die Textfarbe
|
||||
auf gelbem Grund, GELB_HELL die aufgehellte Flaeche fuer Badges. */
|
||||
let TINTE = "#1A1A1A", TEXT2 = "#555555", TEXT3 = "#888888",
|
||||
GELB = "#F1B729", GELB_DUNKEL = "#3E2C0A", GELB_HELL = "#FFE082",
|
||||
TEAL = "#0D6E6E", TEAL_HELL = "#E8F4F4",
|
||||
LINIE = "#E5E5E5", KARTE = "#F6F8FA";
|
||||
function farbenNachziehen() {
|
||||
const d = variant === "dunkel";
|
||||
TINTE = d ? "#F2F6F5" : "#0E1F1E";
|
||||
GELB = "#F1B729";
|
||||
TEAL = d ? "#3FA9A9" : "#0D6E6E";
|
||||
TEXT2 = d ? "#9FB2B0" : "#5B6D6B";
|
||||
LINIE = d ? "rgba(242,246,245,.18)" : "#DCE4E3";
|
||||
KARTE = d ? "rgba(242,246,245,.05)" : "#FFFFFF";
|
||||
TINTE = d ? "#FFFFFF" : "#1A1A1A";
|
||||
TEXT2 = d ? "#C8D0DC" : "#555555";
|
||||
TEXT3 = d ? "#8C97A8" : "#888888";
|
||||
TEAL = d ? "#4FBEBE" : "#0D6E6E";
|
||||
TEAL_HELL = d ? "rgba(79,190,190,.16)" : "#E8F4F4";
|
||||
LINIE = d ? "rgba(255,255,255,.14)" : "#E5E5E5";
|
||||
KARTE = d ? "#1A2740" : "#F6F8FA";
|
||||
}
|
||||
|
||||
/* Der Chamfer der Marke: 16 px Schnitt, wie in lib/chamfer.ts der Website.
|
||||
Dort steht auch, warum eine chamferte Flaeche keinen Rand bekommt --
|
||||
clip-path beschneidet ihn mit. Also fuellen statt umranden. */
|
||||
const CHAMFER_C = 16;
|
||||
const chamferFlaeche = (x, y, w, h, fuell, c) =>
|
||||
'<path d="' + chamfer(x, y, w, h, c == null ? CHAMFER_C : c) + '" fill="' + fuell + '"/>';
|
||||
const zug = (d, o) => {
|
||||
o = o || {};
|
||||
return '<path d="' + d + '" fill="none" stroke="' + (o.f || TINTE) +
|
||||
@@ -2070,7 +2085,7 @@
|
||||
{ t: "Marketing", g: (x, y) => formMegafon(x, y, 0.86) },
|
||||
];
|
||||
const oben = (H - (punkte.length * KH + (punkte.length - 1) * LU)) / 2;
|
||||
const kachelGrund = variant === "dunkel" ? "rgba(63,169,169,.16)" : "#E7F1F0";
|
||||
const kachelGrund = TEAL_HELL;
|
||||
|
||||
let raus = "";
|
||||
punkte.forEach((p, i) => {
|
||||
@@ -2080,15 +2095,13 @@
|
||||
if (i < punkte.length - 1) {
|
||||
const vy = y + KH, vm = vy + LU / 2;
|
||||
raus += zug(strich(mx, vy, mx, vy + LU), { f: LINIE, w: 2 });
|
||||
raus += '<circle cx="' + mx + '" cy="' + n1(vm) + '" r="10" fill="' + KARTE +
|
||||
'" stroke="' + LINIE + '" stroke-width="2"/>';
|
||||
raus += '<circle cx="' + mx + '" cy="' + n1(vm) + '" r="10" fill="' + KARTE + '"/>';
|
||||
raus += zug(strich(mx - 5, vm, mx + 5, vm) + strich(mx, vm - 5, mx, vm + 5),
|
||||
{ f: TEAL, w: 2 });
|
||||
}
|
||||
|
||||
raus += '<path d="' + chamfer(KX, y, KB, KH, 14) + '" fill="' + KARTE +
|
||||
'" stroke="' + LINIE + '" stroke-width="2"/>';
|
||||
raus += '<path d="' + chamfer(KX + 28, y + 26, 64, 64, 10) + '" fill="' + kachelGrund + '"/>';
|
||||
raus += chamferFlaeche(KX, y, KB, KH, KARTE);
|
||||
raus += chamferFlaeche(KX + 28, y + 26, 64, 64, kachelGrund, 10);
|
||||
raus += zug(p.g(KX + 60, y + 58), { f: TEAL, w: 2.6 });
|
||||
raus += '<text x="' + (KX + 118) + '" y="' + n1(my + 12) + '" ' +
|
||||
'font-family="Manrope, sans-serif" font-size="34" font-weight="700" ' +
|
||||
@@ -2109,41 +2122,99 @@
|
||||
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. */
|
||||
/* ─── Slide 6 · So geht es weiter ────────────────────────────────────────
|
||||
Nachbau der Sektion WaitlistNext.tsx von der Website, nicht eine
|
||||
entschraffierte Zeichnung. Alle Werte kommen von dort und sind mit 1,6
|
||||
hochskaliert (die Liste ist dort 560 breit, hier 896):
|
||||
|
||||
Wabe 54 x 48 -> 86 x 77, flat-top wie im clip-path
|
||||
Schiene links 26 -> 43, also die Wabenmitte, 2 px -> 3
|
||||
Abstand 56 -> 90
|
||||
Nummer 13 px -> 21, Sperrung 0,14em
|
||||
Titel 20 px -> 32
|
||||
Text 14,5 px -> 23
|
||||
|
||||
Auf der Website leuchten die Waben nacheinander auf, waehrend man
|
||||
scrollt; der dritte Schritt ist dort blass, weil er noch nicht erreicht
|
||||
ist. Ein Standbild kennt keinen Scrollstand, deshalb sind hier alle drei
|
||||
aktiv. */
|
||||
function visWarteliste() {
|
||||
keim(20260814);
|
||||
const W = 896, H = 730, HX = 66, R = 54, TX = 176, HOEH = R * 0.866;
|
||||
const W = 896, H = 600;
|
||||
const HB = 86, HH = 77, HX = 0, TX = 120, ABSTAND = 84;
|
||||
|
||||
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."] },
|
||||
{ n: "01", t: "Du trägst dich ein", ikon: "brief",
|
||||
z: ["Eine E-Mail genügt. Kein Vertrag,", "keine Kreditkarte, unverbindlich."] },
|
||||
{ n: "02", t: "Wir melden uns", ikon: "chat",
|
||||
z: ["Sobald HiveGrid für deinen Betrieb", "bereit ist, persönlich und ohne Spam."] },
|
||||
{ n: "03", t: "Du bist von Anfang an dabei", ikon: "konfetti",
|
||||
z: ["Unter den Ersten, die HiveGrid", "nutzen und mitgestalten."] },
|
||||
];
|
||||
|
||||
const txt = (x, y, groesse, gewicht, farbe, sperr, inhalt) =>
|
||||
'<text x="' + x + '" y="' + y + '" font-family="Manrope, sans-serif" font-size="' +
|
||||
groesse + '" font-weight="' + gewicht + '" letter-spacing="' + sperr +
|
||||
'" fill="' + farbe + '">' + inhalt + '</text>';
|
||||
const zeilenHoehe = 37, blockHoehe = 142;
|
||||
const oben = (H - (schritte.length * blockHoehe + (schritte.length - 1) * ABSTAND)) / 2;
|
||||
|
||||
// Flat-top-Wabe in den Proportionen des clip-path der Website.
|
||||
const wabeForm = (x, y) =>
|
||||
"M" + n1(x + HB * .25) + "," + y + "H" + n1(x + HB * .75) +
|
||||
"L" + n1(x + HB) + "," + n1(y + HH / 2) +
|
||||
"L" + n1(x + HB * .75) + "," + n1(y + HH) +
|
||||
"H" + n1(x + HB * .25) + "L" + x + "," + n1(y + HH / 2) + "Z";
|
||||
|
||||
const zeichen = {
|
||||
brief: (cx, cy) => {
|
||||
const w = 36, h = 26, x = cx - w / 2, y = cy - h / 2;
|
||||
return '<rect x="' + x + '" y="' + y + '" width="' + w + '" height="' + h +
|
||||
'" rx="4" fill="none" stroke="#171106" stroke-width="2.6"/>' +
|
||||
'<path d="M' + (x + 2) + ',' + (y + 4) + 'L' + cx + ',' + (y + h * .6) +
|
||||
'L' + (x + w - 2) + ',' + (y + 4) + '" fill="none" stroke="#171106" ' +
|
||||
'stroke-width="2.6" stroke-linejoin="round" stroke-linecap="round"/>';
|
||||
},
|
||||
chat: (cx, cy) => {
|
||||
const w = 36, h = 28, x = cx - w / 2, y = cy - h / 2 - 3;
|
||||
return '<rect x="' + x + '" y="' + y + '" width="' + w + '" height="' + h +
|
||||
'" rx="9" fill="none" stroke="#171106" stroke-width="2.6"/>' +
|
||||
'<path d="M' + (x + 9) + ',' + (y + h) + 'l-2,9l11,-9z" fill="#171106"/>' +
|
||||
[-8, 0, 8].map(o => '<circle cx="' + (cx + o) + '" cy="' + (y + h / 2) +
|
||||
'" r="2.4" fill="#171106"/>').join("");
|
||||
},
|
||||
konfetti: (cx, cy) => {
|
||||
const g = '<path d="M' + (cx - 16) + ',' + (cy + 15) + 'L' + (cx - 1) + ',' + (cy - 2) +
|
||||
'L' + (cx + 9) + ',' + (cy + 11) + 'Z" fill="none" stroke="#171106" ' +
|
||||
'stroke-width="2.6" stroke-linejoin="round"/>';
|
||||
let f = "";
|
||||
[[6, -14], [14, -6], [13, -17]].forEach(v => {
|
||||
f += '<circle cx="' + (cx + v[0]) + '" cy="' + (cy + v[1]) + '" r="2.3" fill="#171106"/>';
|
||||
});
|
||||
[[2, -11, 9, -18], [11, -1, 19, -4]].forEach(v => {
|
||||
f += '<path d="M' + (cx + v[0]) + ',' + (cy + v[1]) + 'L' + (cx + v[2]) + ',' +
|
||||
(cy + v[3]) + '" stroke="#171106" stroke-width="2.4" stroke-linecap="round"/>';
|
||||
});
|
||||
return g + f;
|
||||
},
|
||||
};
|
||||
|
||||
let raus = "";
|
||||
const mitte = i => oben + i * (blockHoehe + ABSTAND) + HH / 2;
|
||||
|
||||
// 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 });
|
||||
// Die Schiene liegt hinter den Waben und verbindet ihre Mitten.
|
||||
raus += '<rect x="' + n1(HB / 2 - 1.5) + '" y="' + n1(mitte(0)) + '" width="3" height="' +
|
||||
n1(mitte(schritte.length - 1) - mitte(0)) + '" rx="1.5" fill="' + GELB + '"/>';
|
||||
|
||||
schritte.forEach(sc => {
|
||||
raus += flaeche(wabeEcken(HX, sc.y, R), GELB, .16);
|
||||
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, TEXT2, "0", z);
|
||||
schritte.forEach((sc, i) => {
|
||||
const y = oben + i * (blockHoehe + ABSTAND);
|
||||
raus += '<path d="' + wabeForm(HX, y) + '" fill="' + GELB + '"/>';
|
||||
raus += zeichen[sc.ikon](HX + HB / 2, y + HH / 2);
|
||||
raus += '<text x="' + TX + '" y="' + n1(y + 42) + '" font-family="Manrope, sans-serif" ' +
|
||||
'font-size="21" font-weight="800" letter-spacing="0.14em" fill="' + GELB_DUNKEL + '">' +
|
||||
sc.n + '</text>';
|
||||
raus += '<text x="' + (TX + 52) + '" y="' + n1(y + 42) + '" font-family="Manrope, sans-serif" ' +
|
||||
'font-size="32" font-weight="700" letter-spacing="-0.015em" fill="' + TINTE + '">' +
|
||||
sc.t + '</text>';
|
||||
sc.z.forEach((z, k) => {
|
||||
raus += '<text x="' + TX + '" y="' + n1(y + 94 + k * zeilenHoehe) + '" ' +
|
||||
'font-family="Manrope, sans-serif" font-size="23" font-weight="500" fill="' +
|
||||
TEXT2 + '">' + z + '</text>';
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2151,66 +2222,88 @@
|
||||
'" xmlns="http://www.w3.org/2000/svg">' + raus + '</svg>';
|
||||
}
|
||||
|
||||
/* ─── Slide 5 · Die Loesung ──────────────────────────────────────────────
|
||||
Nachbau des Architektur-Diagramms aus dem Pitch-Paket (Screen 0), nicht
|
||||
eine entschraffierte Wabe. Dort sitzt der Kern als gefuellter Kreis in
|
||||
der Mitte, ein farbiger Ring liegt darum und traegt oben eine Pille mit
|
||||
dem Produktnamen, und die andockenden Module stehen als Kacheln aussen.
|
||||
|
||||
Das Diagramm sagt genau das, was die Ueberschrift sagt: der Kern bleibt,
|
||||
der Ring kommt drumherum. Meine Wabe hat dieselbe Aussage transportiert,
|
||||
aber in einer Form, die es in unserer Software nirgends gibt. */
|
||||
function visVerbinden() {
|
||||
keim(20260812);
|
||||
/* Die Flaeche beginnt an der linken Slide-Kante statt bei 92, damit der
|
||||
Pfeil von Slide 4 dort ankommt. Die Zeichnung rueckt um dieselben 92 px
|
||||
nach rechts, steht also unveraendert auf der Slide. */
|
||||
const W = 988, H = 730, cx = 540, cy = 350, R = 160;
|
||||
const RX = 352, RY = 300;
|
||||
const W = 988, H = 730, CX = 494, CY = 368;
|
||||
const KERN = 104, RING = 202, SATX = 344, SATY = 262, KACHEL = 88;
|
||||
|
||||
// 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 & MCP", ikon: ikonKi, vx: -46 },
|
||||
{ a: 300, name: "Marketing", ikon: ikonMegafon, vx: 46 },
|
||||
{ a: 0, name: "CRM", ikon: ikonCrm },
|
||||
{ a: 60, name: "Vertrieb", ikon: ikonTrichter },
|
||||
{ a: 120, name: "Automatisierung", ikon: ikonAuto },
|
||||
{ a: 180, name: "Workflow-Engine", ikon: ikonWorkflow },
|
||||
const module = [
|
||||
{ a: 240, t: "KI & MCP", ikon: (x, y) => formWorkflow(x, y, 0) },
|
||||
{ a: 300, t: "Marketing", ikon: (x, y) => formMegafon(x, y, 1.0) },
|
||||
{ a: 0, t: "CRM", ikon: (x, y) => glyLeute(x, y) },
|
||||
{ a: 60, t: "Vertrieb", ikon: (x, y) => formTrichter(x, y, 1.0) },
|
||||
{ a: 120, t: "Automatisierung", ikon: (x, y) => glyZahnrad(x, y) },
|
||||
{ a: 180, t: "Workflow-Engine", ikon: (x, y) => formWorkflow(x, y, 1.0) },
|
||||
];
|
||||
// KI hat kein eigenes Zeichen im Satz, deshalb der Funke von Slide 5 alt.
|
||||
module[0].ikon = (x, y) => funkeForm(x, y, 20);
|
||||
|
||||
let speichen = "", knoten = "", bilder = "", schrift = "";
|
||||
let raus = "";
|
||||
|
||||
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;
|
||||
// Der Ring: HiveGrid liegt um den Kern, nicht an seiner Stelle.
|
||||
raus += '<circle cx="' + CX + '" cy="' + CY + '" r="' + RING + '" fill="none" stroke="' +
|
||||
GELB + '" stroke-width="3"/>';
|
||||
|
||||
// Speichen und Kacheln.
|
||||
module.forEach(m => {
|
||||
const r = m.a * Math.PI / 180;
|
||||
const sx = CX + Math.cos(r) * SATX, sy = CY + Math.sin(r) * SATY;
|
||||
const vx = sx - CX, vy = sy - CY, 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 += '<text x="' + n1(sx + (s.vx || 0)) + '" y="' + n1(sy + s.ikon.unten) + '" text-anchor="middle" ' +
|
||||
'font-family="Manrope, sans-serif" font-size="28" font-weight="700" ' +
|
||||
'letter-spacing="-0.01em" fill="' + TINTE + '">' + s.name + '</text>';
|
||||
raus += '<line x1="' + n1(CX + ux * RING) + '" y1="' + n1(CY + uy * RING) +
|
||||
'" x2="' + n1(sx - ux * (KACHEL / 2 + 10)) + '" y2="' + n1(sy - uy * (KACHEL / 2 + 10)) +
|
||||
'" stroke="' + LINIE + '" stroke-width="2"/>';
|
||||
raus += '<circle cx="' + n1(CX + ux * RING) + '" cy="' + n1(CY + uy * RING) +
|
||||
'" r="7" fill="' + GELB + '"/>';
|
||||
raus += chamferFlaeche(sx - KACHEL / 2, sy - KACHEL / 2, KACHEL, KACHEL, TEAL_HELL, 12);
|
||||
raus += zug(m.ikon(sx, sy), { f: TEAL, w: 2.6 });
|
||||
const ty = m.a > 180 ? sy - KACHEL / 2 - 16 : sy + KACHEL / 2 + 34;
|
||||
raus += '<text x="' + n1(sx) + '" y="' + n1(ty) + '" text-anchor="middle" ' +
|
||||
'font-family="Manrope, sans-serif" font-size="24" font-weight="700" ' +
|
||||
'letter-spacing="-0.01em" fill="' + TINTE + '">' + m.t + '</text>';
|
||||
});
|
||||
|
||||
// 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.
|
||||
/* Die Fortsetzung des Pfeils von Slide 4, auf derselben Hoehe: der vordere
|
||||
Teil des Schafts und die Spitze. */
|
||||
/* Die Fuellung folgt der Form: im Schaft konstant, in der Spitze laeuft
|
||||
der Ausschlag auf null zu. */
|
||||
const zHalb = x => x <= 104 ? 14 : Math.max(2, 40 * (1 - (x - 104) / 68));
|
||||
const zulauf =
|
||||
flaeche([[0, 157], [104, 157], [104, 130], [172, 175], [104, 220],
|
||||
[104, 193], [0, 193]], TINTE);
|
||||
// Der Kern. Gefuellt und dunkel, damit er als das Bestehende liest.
|
||||
raus += '<circle cx="' + CX + '" cy="' + CY + '" r="' + KERN + '" fill="' +
|
||||
(variant === "dunkel" ? "#1A2740" : "#1A1A1A") + '"/>';
|
||||
raus += '<text x="' + CX + '" y="' + (CY - 34) + '" text-anchor="middle" ' +
|
||||
'font-family="Manrope, sans-serif" font-size="16" font-weight="700" ' +
|
||||
'letter-spacing="0.18em" fill="rgba(255,255,255,.5)">KERN</text>';
|
||||
["Dein", "Programm"].forEach((z, i) => {
|
||||
raus += '<text x="' + CX + '" y="' + (CY + 6 + i * 34) + '" text-anchor="middle" ' +
|
||||
'font-family="Manrope, sans-serif" font-size="30" font-weight="800" ' +
|
||||
'letter-spacing="-0.02em" fill="#FFFFFF">' + z + '</text>';
|
||||
});
|
||||
|
||||
/* Die Pille auf dem Ring, wie im Pitch-Diagramm. Sie sitzt auf der Bahn
|
||||
und macht damit sichtbar, dass HiveGrid der Ring ist und nicht die
|
||||
Mitte. */
|
||||
const PB = 210, PH = 54, PX = CX - PB / 2, PY = CY - RING - PH / 2;
|
||||
raus += chamferFlaeche(PX, PY, PB, PH, GELB, 12);
|
||||
raus += '<text x="' + CX + '" y="' + n1(PY + 36) + '" text-anchor="middle" ' +
|
||||
'font-family="Manrope, sans-serif" font-size="25" font-weight="800" ' +
|
||||
'letter-spacing="0.1em" fill="' + GELB_DUNKEL + '">HIVEGRID</text>';
|
||||
|
||||
return '<svg viewBox="0 0 ' + W + ' ' + H + '" width="' + W + '" height="' + H +
|
||||
'" xmlns="http://www.w3.org/2000/svg">' +
|
||||
zulauf +
|
||||
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) +
|
||||
'</svg>';
|
||||
'" xmlns="http://www.w3.org/2000/svg">' + raus + '</svg>';
|
||||
}
|
||||
|
||||
/* Vierzackiger Funke fuer KI, praezise gezogen. */
|
||||
function funkeForm(cx, cy, r) {
|
||||
const k = r * 0.3, p = [];
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const a = -Math.PI / 2 + i * Math.PI / 4, rr = i % 2 ? k : r;
|
||||
p.push([cx + Math.cos(a) * rr, cy + Math.sin(a) * rr]);
|
||||
}
|
||||
return glatt(p, true);
|
||||
}
|
||||
|
||||
const VIS = {
|
||||
@@ -2285,7 +2378,7 @@
|
||||
Schriftgröße steuerbar (Bilder, Geräte). */
|
||||
const STANDARD = {
|
||||
"badge": { label: "Tag", size: 26, prop: "font-size" },
|
||||
"headline": { label: "Überschrift", size: 84, prop: "font-size" },
|
||||
"headline": { label: "Überschrift", size: 76, prop: "font-size" },
|
||||
"body": { label: "Beschreibung", size: 30, prop: "font-size" },
|
||||
"wabe-tagline": { label: "Tagline", size: 74, prop: "font-size" },
|
||||
"wabe-claim": { label: "Claim", size: 31, prop: "font-size" },
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"int": "dezent"
|
||||
}
|
||||
],
|
||||
"current": 3,
|
||||
"current": 2,
|
||||
"deck": [
|
||||
{
|
||||
"ref": "titel",
|
||||
|
||||
Reference in new issue
Block a user