Fundament: Branding Craftvia

- Farb-Tokens laut Brandbook §11.4 (Lotsenblau, Signalorange, Graphit, Hafengrau,
  Stahlgrau, Zink, Funktionsfarben) in globals.css + src/lib/brand.ts; helles Theme
- Inline-SVG-Logo src/components/brand/craftvia-logo.tsx (horizontal/signet,
  color/mono/reversed, App-Icon-Kachel, optionale Tagline); Certvia-/GEFIM-Logos entfernt
- Favicon/PWA-Icons aus dem Signet erzeugt (scripts/generate-brand-icons.ts),
  site.webmanifest (Craftvia, theme_color #082E5B)
- Inter selbst gehostet (next/font/local, lokale OFL-Datei), Open Sans/Poppins entfernt
- Metadaten, WebAuthn-RP-Name, Mail-/Dokument-CD auf Craftvia umgestellt
- docs/craftvia/BRANDING.md ersetzt docs/BRANDING-CERTVIA.md

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-14 11:39:52 +02:00
co-authored by Claude Opus 5
parent 8491c7f173
commit 9ec7fa5356
62 changed files with 549 additions and 1070 deletions
+12 -90
View File
@@ -1,10 +1,9 @@
import { cn } from "@/lib/utils";
/**
* Basisbausteine im Look des UI-Mockups (docs/ISMS-Prototyp-GEFIM.html —
* historisches Projektartefakt, der Dateiname bleibt bewusst unverändert):
* PageHead (Crumb/Titel/Sub + Aktionen), KPI-Kapsel, C/I/A-Quadrate,
* Tag-Chip, Owner-Chip, Status-Pille.
* UI-Basisbausteine (Craftvia-CD): PageHead (Crumb/Titel/Sub + Aktionen), KPI-Kapsel,
* Tag-Chip, Owner-Chip, Status-Pille. Farbe nie als einziges Statussignal — Pillen
* tragen immer Text.
*/
export function PageHead({
@@ -56,80 +55,9 @@ export function KpiCard({
);
}
// Segmentbalken-Farben je Stufe 1–4 als Ampel: Grün → Gelb → Orange → Rot
const CIA_BAR_COLORS: Record<number, string> = {
1: "bg-[#2e9e6b]",
2: "bg-[#e5b000]",
3: "bg-[#e07d2e]",
4: "bg-[#d64c4c]",
};
/** Ein Schutzziel als 4er-Segmentbalken, gefüllt bis `level`. */
function CiaSegment({ level, label }: { level: number; label?: string }) {
const color = CIA_BAR_COLORS[level] ?? CIA_BAR_COLORS[1];
return (
<span className="inline-flex flex-col items-center gap-1">
<span className="flex gap-[2px]">
{[1, 2, 3, 4].map((seg) => (
<span
key={seg}
className={cn(
"h-4 w-[7px] rounded-[2px]",
seg <= level ? color : "bg-[rgba(120,135,180,0.2)]"
)}
/>
))}
</span>
{label && (
<small className="text-[9.5px] font-bold tracking-[.04em] text-muted-foreground">
{label}
</small>
)}
</span>
);
}
/**
* Schutzbedarf / Schadenshöhe als drei Segmentbalken (C/I/A). Mit `labels`
* werden VER/INT/VFB unter den Balken gezeigt (Detailkarten); ohne für
* kompakte Tabellenzellen.
*/
export function CiaBadge({
c,
i,
a,
labels = false,
}: {
c: number;
i: number;
a: number;
labels?: boolean;
}) {
return (
<span className="inline-flex items-center gap-2.5" title={`C${c} · I${i} · A${a}`}>
<CiaSegment level={c} label={labels ? "VER" : undefined} />
<CiaSegment level={i} label={labels ? "INT" : undefined} />
<CiaSegment level={a} label={labels ? "VFB" : undefined} />
</span>
);
}
/** VER/INT/VFB-Beschriftung passend zur Balkenbreite — einmal pro Tabelle als Überschrift. */
export function CiaLegend() {
return (
<span className="inline-flex items-center gap-2.5 text-[9.5px] font-bold tracking-[.04em] text-muted-foreground">
{["VER", "INT", "VFB"].map((l) => (
<span key={l} className="w-[34px] text-center">
{l}
</span>
))}
</span>
);
}
export function Tag({ children }: { children: React.ReactNode }) {
return (
<span className="inline-block rounded-md bg-[rgba(139,147,173,0.14)] px-2 py-0.5 text-[11px] font-bold text-[#b7bdd0]">
<span className="inline-block rounded-md bg-[var(--muted)] px-2 py-0.5 text-[11px] font-bold text-muted-foreground">
{children}
</span>
);
@@ -153,15 +81,15 @@ export function OwnerChip({ name, noOwnerLabel }: { name?: string | null; noOwne
);
}
// Status-Chips: ~16–20 % Alpha der Statusfarbe auf Dunkel (Referenz-Vorgabe)
// Status-Chips: ~12 % Alpha der Funktionsfarbe auf hellem Grund (Brandbook §11.4).
const PILL_TONES = {
ok: "bg-[rgba(57,192,127,0.16)] text-[var(--ok)]",
warn: "bg-[rgba(240,173,78,0.16)] text-[var(--warn)]",
orange: "bg-[rgba(240,140,60,0.16)] text-[#f0a35a]",
risk: "bg-[rgba(255,107,107,0.16)] text-[var(--risk)]",
info: "bg-[rgba(90,169,230,0.16)] text-[var(--info)]",
mut: "bg-[rgba(139,147,173,0.16)] text-muted-foreground",
violet: "bg-[rgba(125,111,214,0.2)] text-[#c3bdec]",
ok: "bg-[rgba(35,132,93,0.12)] text-[var(--ok)]",
warn: "bg-[rgba(200,121,18,0.12)] text-[var(--warn)]",
orange: "bg-[rgba(255,106,0,0.12)] text-[#b34a00]",
risk: "bg-[rgba(198,66,66,0.12)] text-[var(--risk)]",
info: "bg-[rgba(47,111,163,0.12)] text-[var(--info)]",
mut: "bg-[rgba(102,114,125,0.12)] text-muted-foreground",
violet: "bg-[rgba(8,46,91,0.1)] text-[var(--primary)]",
} as const;
export function Pill({
@@ -183,12 +111,6 @@ export function Pill({
);
}
/** Kritikalität 1–4 als Pille in Mockup-Farben (info/warn/risk). */
export function CriticalityPill({ level, label }: { level: number; label: string }) {
const tone = level >= 4 ? "risk" : level === 3 ? "warn" : level === 2 ? "info" : "ok";
return <Pill tone={tone}>{label}</Pill>;
}
export function SectTitle({ title, sub }: { title: string; sub?: string }) {
return (
<div>