diff --git a/post2-alt.html b/post2-alt.html
index 86361dd..bf257e5 100644
--- a/post2-alt.html
+++ b/post2-alt.html
@@ -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
auf der Baustelle an.',
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,
hat nicht nur Erfahrung.',
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?
Kannst du vergessen.',
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.
Wir wollten mehr.',
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
bleibt, wo es ist.',
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
für Handwerker',
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) =>
+ '';
const zug = (d, o) => {
o = o || {};
return ' 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 += '';
+ raus += '';
raus += zug(strich(mx - 5, vm, mx + 5, vm) + strich(mx, vm - 5, mx, vm + 5),
{ f: TEAL, w: 2 });
}
- raus += '';
- raus += '';
+ 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 += ' 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) =>
- '' + inhalt + '';
+ 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 '' +
+ '';
+ },
+ chat: (cx, cy) => {
+ const w = 36, h = 28, x = cx - w / 2, y = cy - h / 2 - 3;
+ return '' +
+ '' +
+ [-8, 0, 8].map(o => '').join("");
+ },
+ konfetti: (cx, cy) => {
+ const g = '';
+ let f = "";
+ [[6, -14], [14, -6], [13, -17]].forEach(v => {
+ f += '';
+ });
+ [[2, -11, 9, -18], [11, -1, 19, -4]].forEach(v => {
+ f += '';
+ });
+ 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 += '';
- 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 += '';
+ raus += zeichen[sc.ikon](HX + HB / 2, y + HH / 2);
+ raus += '' +
+ sc.n + '';
+ raus += '' +
+ sc.t + '';
+ sc.z.forEach((z, k) => {
+ raus += '' + z + '';
});
});
@@ -2151,66 +2222,88 @@
'" xmlns="http://www.w3.org/2000/svg">' + raus + '';
}
+ /* ─── 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 += '';
+
+ // 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 += '' + s.name + '';
+ raus += '';
+ raus += '';
+ 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 += '' + m.t + '';
});
- // 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 += '';
+ raus += 'KERN';
+ ["Dein", "Programm"].forEach((z, i) => {
+ raus += '' + z + '';
+ });
+
+ /* 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 += 'HIVEGRID';
return '';
+ '" xmlns="http://www.w3.org/2000/svg">' + raus + '';
+ }
+
+ /* 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" },
diff --git a/state/post2-alt.json b/state/post2-alt.json
index d3a468b..7cb0a36 100644
--- a/state/post2-alt.json
+++ b/state/post2-alt.json
@@ -25,7 +25,7 @@
"int": "dezent"
}
],
- "current": 3,
+ "current": 2,
"deck": [
{
"ref": "titel",