Anelias Feedback: Slogan auf der Naht, ihre Headline, HR auf der Trainings-Seite

Slogan: „Personality · Impact · Success“ steht nicht mehr im Hero unter dem
Monogramm, sondern als dunkle Plakette mit Goldrand mittig auf der Naht zwischen
Cover und Abschnitt 01 – auf beiden Sprachfassungen englisch (lang="en"). Neue
optionale `lead`-Stelle in <Section/>; Inhalt unter `positioning.slogan`. Das
Monogramm hat ab lg wieder seine alte Größe, das Cover ist 2.5 rem kürzer als
der Viewport, damit die Plakette schon auf dem ersten Bildschirm ganz zu sehen
ist. Unter lg hält `pb-10` den Kennenlern-Knopf frei von der halben Plakette.

Headline: Anelias Wortlaut „Mit starker Persönlichkeit und souveräner Wirkung
zum Erfolg!“ – drei Betonungen, Gold-Verlauf nur auf „Erfolg“. Englisch:
„Strong personality, assured impact – your way to success!“. AGENTS.md und
globals.css beschreiben die Trias-Ausnahme jetzt mit dem Slogan auf der Naht.

Trainings: viertes Modul „HR-Teambuilding & Konfliktlösung“ aus ihrer Nachricht
(Dauer offen, Text braucht ihre Freigabe – CONTENT_TODO), vierte Bremse,
Subline und Meta-Beschreibung mit HR; „Konflikte“ wandert aus dem Leadership-
Modul ins HR-Modul. Messeseite (Themenzeile 02) und „Für wen“ auf der
Startseite ziehen mit. Die englische Modultabelle zeigt jetzt englische
Spaltenköpfe (`modules.labels`, „Audience“ passt einzeilig) statt
„Modul | Ziel | Für wen“.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Opus 5.5 committed 2026-10-02 14:51:33 +00:00
1 parent f825e74222
commit 6fdd7df1f8
15 files changed
+193 -116

No files matched your search

+5 -2
View File
@@ -26,9 +26,12 @@ schreibt den Block neu, dieser Teil bleibt).
zusätzlich golden – das macht eine Regel in `globals.css`, nicht die Komponente. zusätzlich golden – das macht eine Regel in `globals.css`, nicht die Komponente.
Auf hellem Grund bleibt es rein typografisch (Gold-Verlauf auf Elfenbein = 2,4:1). Auf hellem Grund bleibt es rein typografisch (Gold-Verlauf auf Elfenbein = 2,4:1).
Einzige Ausnahme: eine Headline, die eine Aufzählung IST – die Trias der Startseite Einzige Ausnahme: eine Headline, die eine Aufzählung IST – die Trias der Startseite
(„Persönlichkeit … Wirkung … Erfolg“), weil sie den Claim unter dem Monogramm („Mit starker *Persönlichkeit* und souveräner *Wirkung* zum *Erfolg*!“, Anelias
wiederholt. Dann trägt jedes Wort ein `<em>`, den Gold-Verlauf aber weiterhin nur Wortlaut), weil sie den Slogan „Personality · Impact · Success“ auf der Naht darunter
aufnimmt. Dann trägt jedes Wort ein `<em>`, den Gold-Verlauf aber weiterhin nur
eines; die übrigen bekommen `em-plain` (`Headline.parts[].gold` im Inhalt steuert das). eines; die übrigen bekommen `em-plain` (`Headline.parts[].gold` im Inhalt steuert das).
- Der Slogan steht NICHT im Hero, sondern als Plakette auf der Naht Cover → 01
(`positioning.slogan`, `<SloganPlate/>` in Positioning.tsx; Entscheidung 02.10.2026).
- Goldene Schrift nur auf `[data-theme="dark"]`; auf Ivory gilt `--gold-deep`. - Goldene Schrift nur auf `[data-theme="dark"]`; auf Ivory gilt `--gold-deep`.
- Sichtbare Copy lebt ausschließlich in `src/content/*.ts`, nie im JSX. - Sichtbare Copy lebt ausschließlich in `src/content/*.ts`, nie im JSX.
+2 -2
View File
@@ -293,8 +293,8 @@ svg {
} }
/* Ausnahme für eine Headline, die eine Aufzählung IST – die Trias der Startseite /* Ausnahme für eine Headline, die eine Aufzählung IST – die Trias der Startseite
(„Persönlichkeit … Wirkung … Erfolg“) betont drei Wörter, weil sie den Claim unter („Persönlichkeit … Wirkung … Erfolg“) betont drei Wörter, weil sie den Slogan auf
dem Monogramm wiederholt. Gold bleibt trotzdem rationiert: den Verlauf trägt genau der Naht darunter aufnimmt. Gold bleibt trotzdem rationiert: den Verlauf trägt genau
das letzte Wort, die übrigen bleiben rein kursiv. Gesetzt über `parts[].gold` im das letzte Wort, die übrigen bleiben rein kursiv. Gesetzt über `parts[].gold` im
Inhalt, nicht in der Komponente. */ Inhalt, nicht in der Komponente. */
[data-theme="dark"] .font-display em.em-plain { [data-theme="dark"] .font-display em.em-plain {
+28 -51
View File
@@ -15,7 +15,6 @@ import { Magnet } from "@/components/motion/Magnet";
import { DrawMark } from "@/components/brand/DrawMark"; import { DrawMark } from "@/components/brand/DrawMark";
import { Button } from "@/components/ui/Button"; import { Button } from "@/components/ui/Button";
import { PlaceholderText } from "@/components/ui/Facts"; import { PlaceholderText } from "@/components/ui/Facts";
import { EYEBROW_TEXT } from "@/components/ui/Eyebrow";
export interface HeroProps { export interface HeroProps {
/** `<BrandMark idPrefix="hero" handoff/>` – Server Component, rendered by page.tsx. */ /** `<BrandMark idPrefix="hero" handoff/>` – Server Component, rendered by page.tsx. */
@@ -28,9 +27,8 @@ export interface HeroProps {
/* Load choreography (spec §6.1), seconds from mount. Masthead hairlines 0 / 0.15 are /* Load choreography (spec §6.1), seconds from mount. Masthead hairlines 0 / 0.15 are
Masthead's own; the folio hairline waits for the neck stroke (draw 0.7 + handoff 1.2–1.4). Masthead's own; the folio hairline waits for the neck stroke (draw 0.7 + handoff 1.2–1.4).
Der Claim liegt dazwischen: erst zeichnet der Strich zu Ende, dann steht das Wort da, Danach erscheint der Slogan auf der Naht (<Positioning/>, Verzögerung 2.3). */
dann läuft die Haarlinie aus. */ const T = { lines: 0.3, door: 0.5, draw: 0.7, cta: 1.2, folio: 2.0 } as const;
const T = { lines: 0.3, door: 0.5, draw: 0.7, cta: 1.2, claim: 1.5, folio: 2.0 } as const;
/** Fallback length of the handoff hairline until it is measured (px). */ /** Fallback length of the handoff hairline until it is measured (px). */
const HANDOFF = 40; const HANDOFF = 40;
@@ -39,8 +37,6 @@ const MONO_11 = "font-mono text-[11px] tracking-[0.06em]";
/** /**
* Handoff x for `<Rule origin={x}/>`, measured from the folio's left edge. * Handoff x for `<Rule origin={x}/>`, measured from the folio's left edge.
* Unverändert vom Claim: der Ursprung der Folio-Haarlinie bleibt der Punkt
* direkt unter dem V, auch wenn der Strich jetzt am Claim endet.
* DrawMark writes `--handoff-x` on its wrapper (fallback WolfMark); a client * DrawMark writes `--handoff-x` on its wrapper (fallback WolfMark); a client
* SVG with `path#handoff` is measured at the path's end point. * SVG with `path#handoff` is measured at the path's end point.
*/ */
@@ -104,24 +100,27 @@ function markBottomY(markWrap: HTMLElement, pts: { x: number; y: number }[]): nu
} }
/** /**
* Cover (ink, ≥ 100svh). Rows: (leer) · 1fr · H1 · Subline + CTA · Folio. * Cover (ink, ≥ 100svh − 2.5rem). Rows: (leer) · 1fr · H1 · Subline + CTA · Folio.
* Die Mastkopf-Zeile wurde am 06.09.2026 auf Kundenwunsch gestrichen; die erste * Die Mastkopf-Zeile wurde am 06.09.2026 auf Kundenwunsch gestrichen; die erste
* Rasterzeile bleibt leer, damit die übrigen Zeilenzuweisungen unverändert gelten. * Rasterzeile bleibt leer, damit die übrigen Zeilenzuweisungen unverändert gelten.
* Variant A (photo): portrait cols 8–12 bleeding to the page edge with scrims, * Variant A (photo): portrait cols 8–12 bleeding to the page edge with scrims,
* monogram 28vh in cols 6–8 overlapping its left edge, neck handoff into the * monogram 28vh in cols 6–8 overlapping its left edge, neck handoff into the
* folio hairline, which draws outward from that x. Variant B (no photo): * folio hairline, which draws outward from that x. Variant B (no photo):
* monogram 40vh cols 7–12, right-aligned, same handoff. Unter dem Monogramm * monogram 45vh cols 7–12, right-aligned, same handoff. Mobile: masthead,
* steht der Claim („Persönlichkeit · Wirkung · Erfolg“) – dafür ist das Zeichen * monogram 12svh, H1, CTAs, portrait 3:4 (≤ 48svh) – no folio row (it costs 69 px
* von 45vh auf 40vh geschrumpft, damit das Cover nicht wächst; der Nackenstrich * the phone cover does not have; Positionierung carries „01 / 06“).
* endet an seiner Oberkante. Mobile: masthead, monogram 12svh, Claim, H1, CTAs, *
* portrait 3:4 (≤ 48svh) – no folio row (it costs 69 px the phone cover does not * Der Slogan („Personality · Impact · Success“) steht seit dem 02.10.2026 nicht mehr
* have; Positionierung carries „01 / 06“). * hier unter dem Monogramm, sondern als Plakette auf der Naht zu Abschnitt 01
* (<Positioning/>). Damit sie schon auf dem ersten Bildschirm ganz zu sehen ist, ist
* das Cover 2.5 rem kürzer als der Viewport – die Plakette ragt halb hinein. Unter lg
* ist der CTA der letzte Block über der Naht: `pb-10` hält ihn frei von der halben
* Plakette (ohne lag sie auf kleinen Handys 8–14 px auf dem Knopf).
*/ */
export function Hero({ mark, photo, content: hero = heroDe }: HeroProps) { export function Hero({ mark, photo, content: hero = heroDe }: HeroProps) {
const root = useRef<HTMLDivElement>(null); const root = useRef<HTMLDivElement>(null);
const h1 = useRef<HTMLDivElement>(null); const h1 = useRef<HTMLDivElement>(null);
const markWrap = useRef<HTMLDivElement>(null); const markWrap = useRef<HTMLDivElement>(null);
const claim = useRef<HTMLDivElement>(null);
const folio = useRef<HTMLDivElement>(null); const folio = useRef<HTMLDivElement>(null);
const [origin, setOrigin] = useState<number | null>(null); const [origin, setOrigin] = useState<number | null>(null);
const [handoffLength, setHandoffLength] = useState(HANDOFF); const [handoffLength, setHandoffLength] = useState(HANDOFF);
@@ -142,13 +141,7 @@ export function Hero({ mark, photo, content: hero = heroDe }: HeroProps) {
if (!pts) pts = sampleMarkPoints(m); if (!pts) pts = sampleMarkPoints(m);
const bottom = markBottomY(m, pts); const bottom = markBottomY(m, pts);
if (bottom !== null) { if (bottom !== null) {
/* Der Strich endet an der Oberkante des Claims, wenn einer da steht – const gap = f.getBoundingClientRect().top - bottom;
sonst wie bisher an der Folio-Haarlinie. Die Vertikale liest sich damit
Nacken → Strich → Claim → Haarlinie, und der Ursprung der Haarlinie
bleibt derselbe x. Der Claim animiert `y`, sein Wrapper nicht: dessen
Kasten steht schon zur Messzeit an der richtigen Stelle. */
const target = claim.current ?? f;
const gap = target.getBoundingClientRect().top - bottom;
setHandoffLength(Math.round(Math.min(72, Math.max(16, gap)))); setHandoffLength(Math.round(Math.min(72, Math.max(16, gap))));
} }
}; };
@@ -196,7 +189,7 @@ export function Hero({ mark, photo, content: hero = heroDe }: HeroProps) {
<Section <Section
id={hero.id} id={hero.id}
theme="dark" theme="dark"
className="grid min-h-svh pt-[calc(var(--header-h,7.5rem)+0.75rem)] pb-0 lg:pt-[calc(var(--header-h,7.5rem)+0.5rem)] [&>div]:grid" className="grid min-h-[calc(100svh-2.5rem)] pt-[calc(var(--header-h,7.5rem)+0.75rem)] pb-10 lg:pb-0 lg:pt-[calc(var(--header-h,7.5rem)+0.5rem)] [&>div]:grid"
> >
{/* From lg the FOURTH row (subline + CTA) is the flexible one, not the second: {/* From lg the FOURTH row (subline + CTA) is the flexible one, not the second:
the slack has to sit between the CTA block and the folio, otherwise a the slack has to sit between the CTA block and the folio, otherwise a
@@ -241,10 +234,8 @@ export function Hero({ mark, photo, content: hero = heroDe }: HeroProps) {
</div> </div>
)} )}
{/* Monogramm + Claim: als Block unten an der Folio-Haarlinie ausgerichtet. {/* Monogramm: unten an der Folio-Haarlinie ausgerichtet; der Nackenstrich
Der Nackenstrich endet am Claim, der Ursprung der Haarlinie bleibt der läuft vom V bis auf die Haarlinie. */}
Punkt direkt unter dem V. `items-end`/`items-start` statt `justify-self`,
weil der Kasten jetzt zwei Kinder trägt und die Spalte füllen muss. */}
<div <div
ref={markWrap} ref={markWrap}
className={cn( className={cn(
@@ -256,7 +247,9 @@ export function Hero({ mark, photo, content: hero = heroDe }: HeroProps) {
hasPhoto ? "lg:col-span-3 lg:col-start-6 lg:items-start" : "lg:col-span-6 lg:col-start-7 lg:items-end", hasPhoto ? "lg:col-span-3 lg:col-start-6 lg:items-start" : "lg:col-span-6 lg:col-start-7 lg:items-end",
)} )}
> >
<div className={cn("h-[12svh]", hasPhoto ? "lg:h-[24vh]" : "lg:h-[min(40vh,22vw)]")}> {/* Ab lg wieder die Größe von vor dem Claim (45vh / 28vh). Am Handy bleiben es
12svh: Der Platz geht an den Abstand über der Slogan-Plakette. */}
<div className={cn("h-[12svh]", hasPhoto ? "lg:h-[28vh]" : "lg:h-[min(45vh,24vw)]")}>
<DrawMark <DrawMark
play="load" play="load"
sheen sheen
@@ -268,20 +261,6 @@ export function Hero({ mark, photo, content: hero = heroDe }: HeroProps) {
{mark} {mark}
</DrawMark> </DrawMark>
</div> </div>
{/* Der Wrapper trägt das Ref und wird NICHT animiert – `<Reveal/>` verschiebt
nur sein eigenes Element, sodass die Messung oben den ruhenden Kasten trifft.
Trennzeichen wie im Ledger von der Komponente gesetzt, nicht aus dem Inhalt. */}
<div ref={claim} className="mt-3 lg:mt-4">
<Reveal play="load" delay={T.claim} as="p" className={cn(EYEBROW_TEXT, "leading-[1.5] text-ivory/75")}>
{hero.claim.map((word, i) => (
<Fragment key={word}>
{i > 0 && <span className="mx-[0.7em] text-gold">·</span>}
{word}
</Fragment>
))}
</Reveal>
</div>
</div> </div>
{/* H1 */} {/* H1 */}
@@ -289,17 +268,15 @@ export function Hero({ mark, photo, content: hero = heroDe }: HeroProps) {
ref={h1} ref={h1}
className="relative z-[2] col-span-12 row-start-3 mt-6 lg:col-span-8 lg:col-start-1 lg:row-start-3 lg:mt-0 lg:self-end" className="relative z-[2] col-span-12 row-start-3 mt-6 lg:col-span-8 lg:col-start-1 lg:row-start-3 lg:mt-0 lg:self-end"
> >
{/* Zwei Maße, beide gemessen statt geschätzt (Playfair bei 100 px: {/* `max-w-[10.9em]` wurde für die Vorgänger-Headline gemessen und passt auch
„Mit deiner Persönlichkeit“ = 1033 px, „entwickelst du deine Wirkung –“ für Anelias Satz (02.10.2026, gemessen): Desktop DE und EN je drei Zeilen
= 1315 px, ganze Zeile = 3314 px): („Mit starker Persönlichkeit | und souveräner | Wirkung zum Erfolg!“),
390 px DE vier, EN drei, 360 px DE fünf. Die erste Zeile ist mit ≈ 10.8 em
`max-w-[10.9em]` liegt zwischen diesen beiden Werten. Damit bricht die knapp unter der Grenze – rendert ein Browser sie breiter, rutscht
Headline auf JEDER Schriftgröße in dieselben vier Zeilen – der Umbruch „Persönlichkeit“ in Zeile 2, was ebenso sauber bricht (`text-wrap:balance`).
hängt nicht mehr an der Fenstergröße. Ab lg begrenzt die Spalte (854 px
bei 1440) auf 10.95 em; auch das liegt noch über 1033.
Der Clamp bleibt zusätzlich von der Fenster-HÖHE gedeckelt: `22vh - 120px` Der Clamp bleibt zusätzlich von der Fenster-HÖHE gedeckelt: `22vh - 120px`
ist bei 1440×900 neutral und zieht die vier Zeilen auf 800/768-px-Laptops ist bei 1440×900 neutral und zieht die Zeilen auf 800/768-px-Laptops
zurück, wo der vw-Term schneller wächst als der Bildschirm schrumpft. */} zurück, wo der vw-Term schneller wächst als der Bildschirm schrumpft. */}
<Lines <Lines
as="h1" as="h1"
@@ -335,7 +312,7 @@ export function Hero({ mark, photo, content: hero = heroDe }: HeroProps) {
</div> </div>
{/* Subline + CTA. Vertical rhythm from lg is viewport-relative so the cover {/* Subline + CTA. Vertical rhythm from lg is viewport-relative so the cover
keeps fitting `min-h-svh` at 768–900 px viewport heights; the row runs keeps fitting its min height (100svh − 2.5rem) at 768–900 px viewport heights; the row runs
cols 1–8 below 1440 so button + text link stay on one line there too. At cols 1–8 below 1440 so button + text link stay on one line there too. At
1440×900 – the cover's reference frame – the original 24/32 px rhythm is 1440×900 – the cover's reference frame – the original 24/32 px rhythm is
restored, which also keeps the headline clear of the monogram. */} restored, which also keeps the headline clear of the monogram. */}
+46 -2
View File
@@ -1,10 +1,11 @@
import { Fragment } from "react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { positioning as positioningDe, type HomeContent } from "@/content/home"; import { positioning as positioningDe, type HomeContent } from "@/content/home";
import { Section } from "@/components/layout/Section"; import { Section } from "@/components/layout/Section";
import { Lines } from "@/components/motion/Lines"; import { Lines } from "@/components/motion/Lines";
import { Reveal, RevealGroup, RevealItem } from "@/components/motion/Reveal"; import { Reveal, RevealGroup, RevealItem } from "@/components/motion/Reveal";
import { Rule } from "@/components/motion/Rule"; import { Rule } from "@/components/motion/Rule";
import { Eyebrow } from "@/components/ui/Eyebrow"; import { Eyebrow, EYEBROW_TEXT } from "@/components/ui/Eyebrow";
/** H2 on ivory: Playfair 400, 36 → 76 px, one italic word max. */ /** H2 on ivory: Playfair 400, 36 → 76 px, one italic word max. */
export const H2 = export const H2 =
@@ -12,6 +13,48 @@ export const H2 =
const MONO = "font-mono text-xs tracking-[0.06em] tabular-nums"; const MONO = "font-mono text-xs tracking-[0.06em] tabular-nums";
/**
* Slogan-Plakette auf der Naht Cover → 01 (Entscheidung 02.10.2026: nicht im Hero,
* sondern „im Übergang“, in einer Zeile). Eine Schrift kann nicht über der Kante die
* Farbe wechseln – deshalb trägt die Plakette ihren eigenen Ink-Grund mit Goldrand:
* Die obere Hälfte verschmilzt mit dem Cover, die untere liegt als Etikett auf Ivory,
* die goldene Naht-Haarlinie läuft dahinter durch. Gold nur auf Dunkel (AGENTS.md).
*
* Aufbau: Die Hülle steht mit Höhe 0 auf der Oberkante und zentriert; das mittlere
* Element hebt die Plakette um die halbe Höhe an (statisch); `<Reveal/>` animiert
* innen nur opacity + y – so streiten Transform und GSAP nicht um dasselbe Element.
* `data-theme="dark"` in einer hellen Sektion malt keinen Grund (globals.css), daher
* `bg-ink` ausdrücklich.
*/
function SloganPlate({ words, lang }: { words: readonly string[]; lang: string }) {
return (
/* `items-start`: ohne streckt Flex das Kind auf die Höhe 0 der Hülle, und das
Anheben um die halbe Höhe liefe ins Leere. */
<div className="pointer-events-none absolute inset-x-0 top-0 z-[3] flex h-0 items-start justify-center px-3">
<div className="-translate-y-1/2">
<Reveal play="load" delay={2.3} y={8}>
<p
lang={lang}
data-theme="dark"
className={cn(
EYEBROW_TEXT,
"whitespace-nowrap border border-gold/60 bg-ink px-5 py-3 leading-[1.5] text-ivory/80 sm:px-7",
"max-[359px]:tracking-[0.18em]",
)}
>
{words.map((word, i) => (
<Fragment key={word}>
{i > 0 && <span className="mx-[0.7em] text-gold">·</span>}
{word}
</Fragment>
))}
</p>
</Reveal>
</div>
</div>
);
}
/** /**
* 01 · Positionierung (ivory, spec §6.2). Numeral cols 1–2 (sticky), eyebrow + * 01 · Positionierung (ivory, spec §6.2). Numeral cols 1–2 (sticky), eyebrow +
* H2 + editorial paragraph + the three question rows cols 3–9 * H2 + editorial paragraph + the three question rows cols 3–9
@@ -24,7 +67,7 @@ export interface PositioningProps {
} }
export function Positioning({ content: positioning = positioningDe }: PositioningProps = {}) { export function Positioning({ content: positioning = positioningDe }: PositioningProps = {}) {
const { numeral, eyebrow, h2, paragraph, rows } = positioning; const { numeral, eyebrow, h2, paragraph, rows, slogan } = positioning;
return ( return (
<Section <Section
@@ -33,6 +76,7 @@ export function Positioning({ content: positioning = positioningDe }: Positionin
numeral={numeral} numeral={numeral}
label={eyebrow} label={eyebrow}
className="py-[clamp(72px,10vh,140px)] md:py-[clamp(112px,16vh,224px)]" className="py-[clamp(72px,10vh,140px)] md:py-[clamp(112px,16vh,224px)]"
lead={<SloganPlate words={slogan.words} lang={slogan.lang} />}
> >
<div className="col-span-12 lg:col-span-7 lg:col-start-3 lg:row-start-1"> <div className="col-span-12 lg:col-span-7 lg:col-start-3 lg:row-start-1">
<Reveal> <Reveal>
+8
View File
@@ -23,6 +23,12 @@ export interface SectionProps {
space?: "chapter" | "block"; space?: "chapter" | "block";
className?: string; className?: string;
as?: "section" | "div" | "article"; as?: "section" | "div" | "article";
/**
* Sitzt außerhalb von Container und Raster direkt an der Oberkante – für etwas, das
* auf der Naht zum Block darüber liegt (Slogan-Plakette der Startseite). Die Sektion
* ist `relative`, das Kind positioniert sich selbst.
*/
lead?: ReactNode;
children: ReactNode; children: ReactNode;
} }
@@ -51,6 +57,7 @@ export function Section({
space = "chapter", space = "chapter",
className, className,
as = "section", as = "section",
lead,
children, children,
}: SectionProps) { }: SectionProps) {
const Tag = as; const Tag = as;
@@ -65,6 +72,7 @@ export function Section({
className, className,
)} )}
> >
{lead}
<Container className="relative"> <Container className="relative">
<div className={GRID}> <div className={GRID}>
{numeral && ( {numeral && (
@@ -26,17 +26,17 @@ const MODULE_TABLE_FIX = [
].join(" "); ].join(" ");
/** /**
* Leistungsbausteine „Drei Module.“ – ledger table cols 3–12: header row * Leistungsbausteine („Vier Module.“) – ledger table cols 3–12: header row
* „Modul | Ziel | Für wen“, one native `<details>` per module (+ → ×), * „Modul | Ziel | Für wen“, one native `<details>` per module (+ → ×),
* opening to the hairline-separated „Inhalte“ list. * opening to the hairline-separated „Inhalte“ list.
*/ */
export function TrainingsModules({ content = trainingsDe }: { content?: TrainingsContent }) { export function TrainingsModules({ content = trainingsDe }: { content?: TrainingsContent }) {
const { eyebrow, h2, items } = content.modules; const { eyebrow, h2, items, labels } = content.modules;
return ( return (
<> <>
<SectionHeading eyebrow={eyebrow} h2={h2} className="col-span-12 lg:col-span-6 lg:col-start-3" /> <SectionHeading eyebrow={eyebrow} h2={h2} className="col-span-12 lg:col-span-6 lg:col-start-3" />
<Reveal className="col-span-12 mt-12 lg:col-span-8 lg:col-start-3 lg:mt-16"> <Reveal className="col-span-12 mt-12 lg:col-span-8 lg:col-start-3 lg:mt-16">
<ModuleTable modules={items} className={MODULE_TABLE_FIX} /> <ModuleTable modules={items} labels={labels} className={MODULE_TABLE_FIX} />
</Reveal> </Reveal>
</> </>
); );
@@ -2,7 +2,7 @@ import { Ledger } from "@/components/ui/Ledger";
import { trainings as trainingsDe, type TrainingsContent } from "@/content/trainings"; import { trainings as trainingsDe, type TrainingsContent } from "@/content/trainings";
import { SectionHeading } from "@/components/services/SectionHeading"; import { SectionHeading } from "@/components/services/SectionHeading";
/** Ausgangslage „Drei Bremsen.“ – H2 cols 3–8, situations ledger cols 3–11 (rows stagger in via Reveal). */ /** Ausgangslage „Vier Bremsen.“ – H2 cols 3–8, situations ledger cols 3–11 (rows stagger in via Reveal). */
export function TrainingsSituations({ content = trainingsDe }: { content?: TrainingsContent }) { export function TrainingsSituations({ content = trainingsDe }: { content?: TrainingsContent }) {
const { eyebrow, h2, rows } = content.situations; const { eyebrow, h2, rows } = content.situations;
return ( return (
+10 -8
View File
@@ -8,6 +8,8 @@ export type { ModuleItem };
export interface ModuleTableProps { export interface ModuleTableProps {
modules: readonly ModuleItem[]; modules: readonly ModuleItem[];
/** Spalten- und Zellbeschriftungen in der Sprache der Seite. Ohne Angabe deutsch. */
labels?: { module: string; goal: string; contents: string; audience: string; meta: string };
className?: string; className?: string;
} }
@@ -21,14 +23,14 @@ const HEAD = cn(MONO, "uppercase tracking-[0.12em] text-accent");
* (Bodoni 30 px title, Jost goal + audience, mono meta). Opens to the * (Bodoni 30 px title, Jost goal + audience, mono meta). Opens to the
* hairline-separated „Inhalte“ list; „+“ rotates to „ד via CSS. * hairline-separated „Inhalte“ list; „+“ rotates to „ד via CSS.
*/ */
export function ModuleTable({ modules, className }: ModuleTableProps) { export function ModuleTable({ modules, labels = moduleLabels, className }: ModuleTableProps) {
const cols = "grid grid-cols-[1fr_auto] gap-x-6 gap-y-3 lg:grid-cols-[minmax(0,3.5fr)_minmax(0,3fr)_minmax(0,2fr)_minmax(0,2.2fr)_2rem] lg:gap-x-8"; const cols = "grid grid-cols-[1fr_auto] gap-x-6 gap-y-3 lg:grid-cols-[minmax(0,3.5fr)_minmax(0,3fr)_minmax(0,2fr)_minmax(0,2.2fr)_2rem] lg:gap-x-8";
return ( return (
<div className={cn("w-full", className)}> <div className={cn("w-full", className)}>
<div className={cn(cols, "hidden border-b border-line pb-3 lg:grid")} aria-hidden="true"> <div className={cn(cols, "hidden border-b border-line pb-3 lg:grid")} aria-hidden="true">
<span className={HEAD}>{moduleLabels.module}</span> <span className={HEAD}>{labels.module}</span>
<span className={HEAD}>{moduleLabels.goal}</span> <span className={HEAD}>{labels.goal}</span>
<span className={HEAD}>{moduleLabels.audience}</span> <span className={HEAD}>{labels.audience}</span>
<span /> <span />
<span /> <span />
</div> </div>
@@ -47,21 +49,21 @@ export function ModuleTable({ modules, className }: ModuleTableProps) {
would be announced as unlabelled text. The label stays in the DOM at every would be announced as unlabelled text. The label stays in the DOM at every
width and is only visually suppressed where the header row takes over. */} width and is only visually suppressed where the header row takes over. */}
<span className="col-span-2 font-sans text-[1.0625rem] leading-snug text-fg lg:col-span-1"> <span className="col-span-2 font-sans text-[1.0625rem] leading-snug text-fg lg:col-span-1">
<span className={cn(HEAD, "mr-3 lg:sr-only")}>{moduleLabels.goal}</span> <span className={cn(HEAD, "mr-3 lg:sr-only")}>{labels.goal}</span>
{m.goal} {m.goal}
</span> </span>
<span className="col-span-2 font-sans text-[1.0625rem] leading-snug text-fg lg:col-span-1"> <span className="col-span-2 font-sans text-[1.0625rem] leading-snug text-fg lg:col-span-1">
<span className={cn(HEAD, "mr-3 lg:sr-only")}>{moduleLabels.audience}</span> <span className={cn(HEAD, "mr-3 lg:sr-only")}>{labels.audience}</span>
{m.audience} {m.audience}
</span> </span>
<span className={cn(MONO, "col-span-2 tabular-nums text-fg-muted lg:col-span-1 lg:text-right")}> <span className={cn(MONO, "col-span-2 tabular-nums text-fg-muted lg:col-span-1 lg:text-right")}>
{/* The meta column has no visible header at any width. */} {/* The meta column has no visible header at any width. */}
<span className="sr-only">{moduleLabels.meta}</span> <span className="sr-only">{labels.meta}</span>
<MetaLines text={m.meta} /> <MetaLines text={m.meta} />
</span> </span>
</summary> </summary>
<div className="pb-8 lg:grid lg:grid-cols-[minmax(0,3fr)_minmax(0,7fr)_2rem] lg:gap-x-8"> <div className="pb-8 lg:grid lg:grid-cols-[minmax(0,3fr)_minmax(0,7fr)_2rem] lg:gap-x-8">
<span className={cn(HEAD, "mb-3 block lg:mb-0")}>{moduleLabels.contents}</span> <span className={cn(HEAD, "mb-3 block lg:mb-0")}>{labels.contents}</span>
<ul className="border-t border-line"> <ul className="border-t border-line">
{m.contents.map((line) => ( {m.contents.map((line) => (
<li key={line} className="border-b border-line py-3 font-sans text-[1.0625rem] leading-snug text-fg"> <li key={line} className="border-b border-line py-3 font-sans text-[1.0625rem] leading-snug text-fg">
+2 -2
View File
@@ -91,8 +91,8 @@ export const herCareerEn: HerCareerContent = {
numeral: "02", numeral: "02",
title: "Training & Workshops", title: "Training & Workshops",
oneLiner: oneLiner:
"B2B sales training: revenue growth through new business and existing customers, communication that fits the type across the team.", "B2B sales training and HR team building: revenue growth through new business and existing customers, team spirit and conflict resolution in the team.",
keywords: ["New business", "Existing customers", "Team communication"], keywords: ["New business", "Existing customers", "HR team building & conflict resolution"],
link: { label: "See training", href: main(r.trainings) }, link: { label: "See training", href: main(r.trainings) },
}, },
{ {
+12 -11
View File
@@ -21,21 +21,21 @@ export const homeEn: HomeContent = {
center: "Sales Mentoring & Potential Development", center: "Sales Mentoring & Potential Development",
lang: "German & English", lang: "German & English",
}, },
/* English rendering of Anelia's German line „Mit starker Persönlichkeit und
souveräner Wirkung zum Erfolg!“ – same three emphases, gold on „success“ only. */
h1: { h1: {
before: "With your personality you shape your impact – and your ", before: "Strong personality, assured impact – your way to ",
em: "success", em: "success",
after: ".", after: "!",
parts: [ parts: [
{ text: "With your " }, { text: "Strong " },
{ text: "personality", em: true }, { text: "personality", em: true, tail: "," },
{ text: " you shape your " }, { text: " assured " },
{ text: "impact", em: true, tail: " –" }, { text: "impact", em: true, tail: " –" },
{ text: " and your " }, { text: " your way to " },
{ text: "success", em: true, gold: true }, { text: "success", em: true, gold: true, tail: "!" },
{ text: "." },
], ],
}, },
claim: ["Personality", "Impact", "Success"],
subline: subline:
"Welcome. Whether as a speaker at your company event, in one-to-one coaching or as a trainer for your team – I am glad to hear what you need.", "Welcome. Whether as a speaker at your company event, in one-to-one coaching or as a trainer for your team – I am glad to hear what you need.",
cta: { cta: {
@@ -51,6 +51,7 @@ export const homeEn: HomeContent = {
/* ---------- 01 Positioning (ivory) ---------- */ /* ---------- 01 Positioning (ivory) ---------- */
positioning: { positioning: {
id: "positionierung", id: "positionierung",
slogan: { words: ["Personality", "Impact", "Success"], lang: "en" },
numeral: "01", numeral: "01",
eyebrow: "Positioning", eyebrow: "Positioning",
h2: { before: "Three questions. One principle.", em: "", after: "" }, h2: { before: "Three questions. One principle.", em: "", after: "" },
@@ -160,11 +161,11 @@ export const homeEn: HomeContent = {
columns: [ columns: [
{ {
label: "Companies", label: "Companies",
title: "Sales teams & their leaders", title: "Sales teams, leaders & HR",
lines: [ lines: [
"New business that becomes predictable", "New business that becomes predictable",
"Existing customers that grow instead of drifting away", "Existing customers that grow instead of drifting away",
"Leadership and team dynamics in sales", "Team spirit & conflict resolution – with leaders and HR",
], ],
link: { label: "See training", href: r.trainings }, link: { label: "See training", href: r.trainings },
}, },
+15 -6
View File
@@ -10,7 +10,7 @@ export const trainingsEn: TrainingsContent = {
meta: { meta: {
title: "Training & Workshops", title: "Training & Workshops",
description: description:
"B2B sales training and leadership workshops that hold up in the working week, not just in the seminar room. In house, online, in German or English. Anelia Wolf, trainer.", "B2B sales training, leadership workshops and HR team building that hold up in the working week, not just in the seminar room. In house, online, in German or English. Anelia Wolf, trainer.",
}, },
numeral: "02", numeral: "02",
masthead: { masthead: {
@@ -21,7 +21,7 @@ export const trainingsEn: TrainingsContent = {
eyebrow: "For companies & teams", eyebrow: "For companies & teams",
h1: { before: "Sales with ", em: "personality", after: "." }, h1: { before: "Sales with ", em: "personality", after: "." },
subline: subline:
"B2B sales training and leadership workshops that hold up in the working week, not just in the seminar room.", "B2B sales training, leadership workshops and HR team building that hold up in the working week, not just in the seminar room.",
cta: { label: "Request training", href: `${routesByLang.en.trainings}#terminanfrage`, micro: "No obligation" }, cta: { label: "Request training", href: `${routesByLang.en.trainings}#terminanfrage`, micro: "No obligation" },
facts: [ facts: [
{ label: "Format", value: "In house or online" }, { label: "Format", value: "In house or online" },
@@ -29,23 +29,25 @@ export const trainingsEn: TrainingsContent = {
{ label: "Language", value: "German / English" }, { label: "Language", value: "German / English" },
{ label: "Group size", value: ph("Platzhalter"), placeholder: true }, { label: "Group size", value: ph("Platzhalter"), placeholder: true },
], ],
folio: { next: { label: "Three brakes", href: "#ausgangslage" } }, folio: { next: { label: "Four brakes", href: "#ausgangslage" } },
situations: { situations: {
id: "ausgangslage", id: "ausgangslage",
eyebrow: "Starting point", eyebrow: "Starting point",
h2: { before: "Three brakes.", em: "", after: "" }, h2: { before: "Four brakes.", em: "", after: "" },
rows: [ rows: [
{ numeral: "01", text: "New business has stalled – and nobody enjoys calling cold." }, { numeral: "01", text: "New business has stalled – and nobody enjoys calling cold." },
{ numeral: "02", text: "Existing customers are looked after, but never grown." }, { numeral: "02", text: "Existing customers are looked after, but never grown." },
{ numeral: "03", text: "Leadership and team dynamics slow sales down instead of carrying it." }, { numeral: "03", text: "Leadership and team dynamics slow sales down instead of carrying it." },
{ numeral: "04", text: "People in the team talk past each other – conflicts linger and team spirit is missing." },
], ],
}, },
modules: { modules: {
id: "module", id: "module",
eyebrow: "What you get", eyebrow: "What you get",
h2: { before: "Three modules.", em: "", after: "" }, h2: { before: "Four modules.", em: "", after: "" },
labels: { module: "Module", goal: "Goal", contents: "What it covers", audience: "Audience", meta: "Format" },
items: [ items: [
{ {
title: "B2B sales training", title: "B2B sales training",
@@ -64,10 +66,17 @@ export const trainingsEn: TrainingsContent = {
{ {
title: "Leadership & team dynamics", title: "Leadership & team dynamics",
goal: "Leadership that makes sales teams stronger.", goal: "Leadership that makes sales teams stronger.",
contents: ["Behaviour profiles in the team", "Roles", "Feedback", "Conflict", "Setting targets"], contents: ["Behaviour profiles in the team", "Roles", "Feedback", "Setting targets"],
audience: "Sales leadership, team leads, management", audience: "Sales leadership, team leads, management",
meta: "One-day workshop · on site · DE/EN", meta: "One-day workshop · on site · DE/EN",
}, },
{
title: "HR team building & conflict resolution",
goal: "A team that knows and understands each other – and resolves conflict itself.",
contents: ["Understanding yourself and the other person", "Communication within the team", "Building team spirit", "Resolving conflict"],
audience: "Teams, leaders and HR",
meta: `Workshop ${ph("Dauer")} · in house · DE/EN`,
},
], ],
}, },
+2 -2
View File
@@ -136,8 +136,8 @@ export const herCareer = {
numeral: "02", numeral: "02",
title: "Trainings & Workshops", title: "Trainings & Workshops",
oneLiner: oneLiner:
"B2B-Vertriebstraining: Umsatzwachstum durch Neukundengewinnung und Bestandskundenaufbau, typgerechte Kommunikation im Team.", "B2B-Vertriebstraining und HR-Teambuilding: Umsatzwachstum durch Neu- und Bestandskunden, Teamgeist und Konfliktlösung im Team.",
keywords: ["Neukundengewinnung", "Bestandskunden", "Teamkommunikation"], keywords: ["Neukundengewinnung", "Bestandskunden", "HR-Teambuilding & Konfliktlösung"],
link: { label: "Zu den Trainings", href: main(routes.trainings) }, link: { label: "Zu den Trainings", href: main(routes.trainings) },
}, },
{ {
+28 -18
View File
@@ -120,6 +120,24 @@ export interface FaqItem {
/* ---------- Hero (Cover, ink) ---------- */ /* ---------- Hero (Cover, ink) ---------- */
/* Hero-Headline – Wortlaut von Anelia (Kundenwunsch, freigegeben 02.10.2026). Drei
Betonungen, den Gold-Verlauf trägt nur „Erfolg“ (Trias-Ausnahme in AGENTS.md).
Ausdrücklich als `Headline` getippt: Die englische Fassung braucht andere
`parts`-Formen (Komma als `tail`), `Widen<typeof hero>` übernähme sonst die deutschen. */
const heroH1: Headline = {
before: "Mit starker Persönlichkeit und souveräner Wirkung zum ",
em: "Erfolg",
after: "!",
parts: [
{ text: "Mit starker " },
{ text: "Persönlichkeit", em: true },
{ text: " und souveräner " },
{ text: "Wirkung", em: true },
{ text: " zum " },
{ text: "Erfolg", em: true, gold: true, tail: "!" },
],
};
export const hero = { export const hero = {
id: "hero", id: "hero",
masthead: { masthead: {
@@ -127,22 +145,7 @@ export const hero = {
center: "Sales Mentoring & Potential Development", center: "Sales Mentoring & Potential Development",
lang: "Deutsch & Englisch", lang: "Deutsch & Englisch",
}, },
h1: { h1: heroH1,
before: "Mit deiner Persönlichkeit entwickelst du deine Wirkung – und deinen ",
em: "Erfolg",
after: ".",
parts: [
{ text: "Mit deiner " },
{ text: "Persönlichkeit", em: true },
{ text: " entwickelst du deine " },
{ text: "Wirkung", em: true, tail: " –" },
{ text: " und deinen " },
{ text: "Erfolg", em: true, gold: true },
{ text: "." },
],
} satisfies Headline,
/** Claim unter dem Monogramm. Das Trennzeichen setzt der Hero, wie im Ledger. */
claim: ["Persönlichkeit", "Wirkung", "Erfolg"],
subline: subline:
"Herzlich willkommen. Ob als Speakerin für dein Firmenevent, im 1:1 Coaching oder als Trainerin für dein Team – gerne bin ich für dein Anliegen da.", "Herzlich willkommen. Ob als Speakerin für dein Firmenevent, im 1:1 Coaching oder als Trainerin für dein Team – gerne bin ich für dein Anliegen da.",
cta: { cta: {
@@ -159,6 +162,13 @@ export const hero = {
export const positioning = { export const positioning = {
id: "positionierung", id: "positionierung",
/**
* Slogan-Plakette auf der Naht zwischen Cover (ink) und diesem Abschnitt – nicht im
* Hero (Entscheidung 02.10.2026). Auf beiden Sprachfassungen englisch, wie Anelia
* ihn formuliert hat; `lang` sagt das dem Screenreader. Das Trennzeichen setzt die
* Komponente.
*/
slogan: { words: ["Personality", "Impact", "Success"], lang: "en" },
numeral: "01", numeral: "01",
eyebrow: "Positionierung", eyebrow: "Positionierung",
h2: { before: "Drei Fragen. Ein Prinzip.", em: "", after: "" } satisfies Headline, h2: { before: "Drei Fragen. Ein Prinzip.", em: "", after: "" } satisfies Headline,
@@ -299,11 +309,11 @@ export const audience = {
columns: [ columns: [
{ {
label: "Unternehmen", label: "Unternehmen",
title: "Vertriebsteams & Führungskräfte", title: "Vertriebsteams, Führung & HR",
lines: [ lines: [
"Neukundengewinnung, die planbar wird", "Neukundengewinnung, die planbar wird",
"Bestandskunden, die wachsen statt abwandern", "Bestandskunden, die wachsen statt abwandern",
"Führung und Teamdynamik im Vertrieb", "Teamgeist & Konfliktlösung – mit Führung und HR",
], ],
link: { label: "Zu den Trainings", href: routes.trainings }, link: { label: "Zu den Trainings", href: routes.trainings },
}, },
+29 -7
View File
@@ -4,6 +4,9 @@
* Rahmen-Abschnitt (stand schon im Hero), die Randspalte im Ablauf (wiederholte die * Rahmen-Abschnitt (stand schon im Hero), die Randspalte im Ablauf (wiederholte die
* Schritte) und die FAQ zur Teilnehmerzahl sind entfallen; statt des Kontakt-Bands * Schritte) und die FAQ zur Teilnehmerzahl sind entfallen; statt des Kontakt-Bands
* mit mailto-Knopf schließt die Seite mit der Terminanfrage (Formular). * mit mailto-Knopf schließt die Seite mit der Terminanfrage (Formular).
* HR dazu am 02.10.2026 (Anelias Wunsch, wie Zeile 02 der Startseite): viertes Modul
* „HR-Teambuilding & Konfliktlösung“, vierte Bremse, Subline und Beschreibung. Der
* Modultext ist aus ihrer Nachricht formuliert und braucht ihre Freigabe (CONTENT_TODO).
* „Antwort innerhalb von 24 Stunden“ bleibt weg (flags.show24hPromise: false). * „Antwort innerhalb von 24 Stunden“ bleibt weg (flags.show24hPromise: false).
* FAQ-Antworten: kurz, aus den Spec-Fakten abgeleitet; Preise „auf Anfrage“ (Spec A.8). * FAQ-Antworten: kurz, aus den Spec-Fakten abgeleitet; Preise „auf Anfrage“ (Spec A.8).
*/ */
@@ -12,7 +15,11 @@ import type { Widen } from "./widen";
import { assertNoPlaceholders, collectStrings, ph } from "@/lib/legal"; import { assertNoPlaceholders, collectStrings, ph } from "@/lib/legal";
import type { Cta, FactItem, FaqItem, Headline, SituationRow, StepItem } from "./home"; import type { Cta, FactItem, FaqItem, Headline, SituationRow, StepItem } from "./home";
/** Kopfzeile der Modul-Tabelle – von <ModuleTable/> importiert. */ /**
* Kopfzeile der Modul-Tabelle (deutsch). Liegt zusätzlich als `modules.labels` im
* Inhalt, damit die englische Seite ihre eigenen Köpfe bekommt – bis 02.10.2026 las
* <ModuleTable/> nur diese Konstante, und /en/ zeigte „Modul | Ziel | Für wen“.
*/
export const moduleLabels = { module: "Modul", goal: "Ziel", contents: "Inhalte", audience: "Für wen", /** Only ever announced – the meta column carries no visible header. */ meta: "Format" } as const; export const moduleLabels = { module: "Modul", goal: "Ziel", contents: "Inhalte", audience: "Für wen", /** Only ever announced – the meta column carries no visible header. */ meta: "Format" } as const;
export interface ModuleItem { export interface ModuleItem {
@@ -27,7 +34,7 @@ export const trainings = {
meta: { meta: {
title: "Trainings & Workshops", title: "Trainings & Workshops",
description: description:
"B2B-Vertriebstrainings und Leadership-Workshops, die im Alltag funktionieren – nicht nur im Seminarraum. Inhouse, online, auf Deutsch oder Englisch. Anelia Wolf, Trainerin.", "B2B-Vertriebstrainings, Leadership-Workshops und HR-Teambuilding, die im Alltag funktionieren – nicht nur im Seminarraum. Inhouse, online, auf Deutsch oder Englisch. Anelia Wolf, Trainerin.",
}, },
numeral: "02", numeral: "02",
masthead: { masthead: {
@@ -38,7 +45,7 @@ export const trainings = {
eyebrow: "Für Unternehmen & Teams", eyebrow: "Für Unternehmen & Teams",
h1: { before: "Vertrieb mit ", em: "Persönlichkeit", after: "." } satisfies Headline, h1: { before: "Vertrieb mit ", em: "Persönlichkeit", after: "." } satisfies Headline,
subline: subline:
"B2B-Vertriebstrainings und Leadership-Workshops, die im Alltag funktionieren – nicht nur im Seminarraum.", "B2B-Vertriebstrainings, Leadership-Workshops und HR-Teambuilding, die im Alltag funktionieren – nicht nur im Seminarraum.",
/* Springt zur Terminanfrage am Fuß der Seite (vorher: mailto). */ /* Springt zur Terminanfrage am Fuß der Seite (vorher: mailto). */
cta: { label: "Training anfragen", href: `${routes.trainings}#terminanfrage`, micro: "Unverbindlich" } satisfies Cta, cta: { label: "Training anfragen", href: `${routes.trainings}#terminanfrage`, micro: "Unverbindlich" } satisfies Cta,
facts: [ facts: [
@@ -47,23 +54,25 @@ export const trainings = {
{ label: "Sprache", value: "Deutsch / Englisch" }, { label: "Sprache", value: "Deutsch / Englisch" },
{ label: "Gruppengröße", value: ph("Platzhalter"), placeholder: true }, { label: "Gruppengröße", value: ph("Platzhalter"), placeholder: true },
] satisfies readonly FactItem[], ] satisfies readonly FactItem[],
folio: { next: { label: "Drei Bremsen", href: "#ausgangslage" } }, folio: { next: { label: "Vier Bremsen", href: "#ausgangslage" } },
situations: { situations: {
id: "ausgangslage", id: "ausgangslage",
eyebrow: "Ausgangslage", eyebrow: "Ausgangslage",
h2: { before: "Drei Bremsen.", em: "", after: "" } satisfies Headline, h2: { before: "Vier Bremsen.", em: "", after: "" } satisfies Headline,
rows: [ rows: [
{ numeral: "01", text: "Die Neukundengewinnung stockt – und niemand ruft gern kalt an." }, { numeral: "01", text: "Die Neukundengewinnung stockt – und niemand ruft gern kalt an." },
{ numeral: "02", text: "Bestandskunden werden betreut, aber nicht ausgebaut." }, { numeral: "02", text: "Bestandskunden werden betreut, aber nicht ausgebaut." },
{ numeral: "03", text: "Führung und Teamdynamik bremsen den Vertrieb, statt ihn zu tragen." }, { numeral: "03", text: "Führung und Teamdynamik bremsen den Vertrieb, statt ihn zu tragen." },
{ numeral: "04", text: "Im Team wird aneinander vorbeigeredet – Konflikte bleiben liegen, der Teamgeist fehlt." },
] satisfies readonly SituationRow[], ] satisfies readonly SituationRow[],
}, },
modules: { modules: {
id: "module", id: "module",
eyebrow: "Leistungsbausteine", eyebrow: "Leistungsbausteine",
h2: { before: "Drei Module.", em: "", after: "" } satisfies Headline, h2: { before: "Vier Module.", em: "", after: "" } satisfies Headline,
labels: moduleLabels,
items: [ items: [
{ {
/* U+2011 (non-breaking hyphen): the Bodoni title must never break after „B2B-“. */ /* U+2011 (non-breaking hyphen): the Bodoni title must never break after „B2B-“. */
@@ -83,10 +92,23 @@ export const trainings = {
{ {
title: "Leadership & Teamdynamik", title: "Leadership & Teamdynamik",
goal: "Führung, die Vertriebsteams stärkt.", goal: "Führung, die Vertriebsteams stärkt.",
contents: ["Verhaltensanalysen im Team", "Rollen", "Feedback", "Konflikte", "Zielvereinbarung"], /* „Konflikte“ ist am 02.10.2026 ins HR-Modul gewandert – dort ist es der Kern. */
contents: ["Verhaltensanalysen im Team", "Rollen", "Feedback", "Zielvereinbarung"],
audience: "Vertriebsleitung, Teamleitung, Geschäftsführung", audience: "Vertriebsleitung, Teamleitung, Geschäftsführung",
meta: "Workshop 1 Tag · vor Ort · DE/EN", meta: "Workshop 1 Tag · vor Ort · DE/EN",
}, },
{
/* Aus Anelias Nachricht (02.10.2026): das Team lernt sich besser kennen, Teamgeist
entsteht, weil man sich und sein Gegenüber versteht – und Konflikte löst.
Bewusst ein normaler Bindestrich (kein U+2011): „HR‑Teambuilding“ am Stück
lief zwischen 1024 und 1320 px in die Ziel-Spalte. Dauer und Freigabe:
CONTENT_TODO. */
title: "HR-Teambuilding & Konfliktlösung",
goal: "Ein Team, das sich kennt und versteht – und Konflikte selbst löst.",
contents: ["Sich und das Gegenüber verstehen", "Kommunikation untereinander", "Teamgeist entwickeln", "Konflikte lösen"],
audience: "Teams, Führungskräfte und HR",
meta: `Workshop ${ph("Dauer")} · inhouse · DE/EN`,
},
] satisfies readonly ModuleItem[], ] satisfies readonly ModuleItem[],
}, },
+2 -1
View File
@@ -104,6 +104,7 @@ export const CONTENT_TODO: string[] = [
"Coaching, Verhaltensanalyse: Dauer des Auswertungsgesprächs und Tool/Zertifizierung eintragen (src/content/coaching.ts).", "Coaching, Verhaltensanalyse: Dauer des Auswertungsgesprächs und Tool/Zertifizierung eintragen (src/content/coaching.ts).",
"Coaching, Meta-Zeile der Analyse: Dauer eintragen (src/content/coaching.ts).", "Coaching, Meta-Zeile der Analyse: Dauer eintragen (src/content/coaching.ts).",
"Coaching, Rahmen-Tabelle: Dauer und Paket eintragen (src/content/coaching.ts).", "Coaching, Rahmen-Tabelle: Dauer und Paket eintragen (src/content/coaching.ts).",
"Trainings, Rahmen-Tabelle (Hero und Rahmen-Abschnitt): Gruppengröße eintragen (src/content/trainings.ts).", "Trainings, Fakten im Hero: Gruppengröße eintragen (src/content/trainings.ts; der Rahmen-Abschnitt ist seit 02.10.2026 entfallen).",
"Trainings, Modul „Neukundengewinnung“: Umfang der Trainingsreihe eintragen (src/content/trainings.ts).", "Trainings, Modul „Neukundengewinnung“: Umfang der Trainingsreihe eintragen (src/content/trainings.ts).",
"Trainings, Modul „HR-Teambuilding & Konfliktlösung“ (seit 02.10.2026): Dauer eintragen und den aus Anelias Nachricht formulierten Modultext von ihr freigeben lassen (src/content/trainings.ts, src/content/en/trainings.ts).",
]; ];