// SloppyUniverse Logo System — v3
// ------------------------------------------------------------
// Two shapes share the same frame geometry:
// • Umbrella → 4-color frame + "UNIV/ERSE" wordmark (main brand)
// • Module → monochrome frame + big single letter (F, S, P, R)
// Colors pulled from the module icons on the login page so everything
// reads as one family.
// Brand palette — aligned to module icons on the login page.
const SU_COLORS = {
// Umbrella quadrant colors — same hues as the modules
green: "#5A7C2A", // SloppyFix (olive)
navy: "#2E3CC6", // SloppyScan (royal blue)
magenta: "#B63E8E", // SloppyPlan (pink/magenta)
orange: "#C86A2E", // SloppyRent (terracotta)
// Text / utility
ink: "#0F1233",
paper: "#FFFFFF",
outline:"#0F1233",
};
// Per-module tokens (frame color = dark variant, accent = light variant for glyph bg)
const SU_MODULES = {
fix: { name: "SloppyFix", letter: "F", pair: "FX", color: "#5A7C2A", tint: "#8FB055" },
scan: { name: "SloppyScan", letter: "S", pair: "SN", color: "#2E3CC6", tint: "#6A78E8" },
plan: { name: "SloppyPlan", letter: "P", pair: "PN", color: "#B63E8E", tint: "#D97AB8" },
rent: { name: "SloppyRent", letter: "R", pair: "RT", color: "#C86A2E", tint: "#E39A68" },
pay: { name: "SloppyPay", letter: "P", pair: "PY", color: "#A47913", tint: "#D9A93B" },
};
// Font presets for the module monogram. `size1/size2` are fractions of icon size.
const FONT_PRESETS = {
"space-grotesk": {
label: "Space Grotesk 600",
family: "'Space Grotesk', system-ui, sans-serif",
weight: 600, size1: 0.44, size2: 0.32, ls1: "-0.02em", ls2: "-0.04em",
},
"ibm-plex-mono": {
label: "IBM Plex Mono 600",
family: "'IBM Plex Mono', ui-monospace, monospace",
weight: 600, size1: 0.44, size2: 0.30, ls1: "0", ls2: "-0.05em",
},
"jetbrains-mono": {
label: "JetBrains Mono 700",
family: "'JetBrains Mono', ui-monospace, monospace",
weight: 700, size1: 0.44, size2: 0.30, ls1: "0", ls2: "-0.05em",
},
"instrument-serif": {
label: "Instrument Serif",
family: "'Instrument Serif', 'Times New Roman', serif",
weight: 400, size1: 0.55, size2: 0.40, ls1: "-0.01em", ls2: "-0.03em",
style: "italic",
},
"archivo-narrow": {
label: "Archivo Narrow 700",
family: "'Archivo Narrow', 'Arial Narrow', sans-serif",
weight: 700, size1: 0.48, size2: 0.38, ls1: "-0.02em", ls2: "-0.04em",
},
"dm-mono": {
label: "DM Mono 500",
family: "'DM Mono', ui-monospace, monospace",
weight: 500, size1: 0.44, size2: 0.30, ls1: "0", ls2: "-0.04em",
},
"inter-tight": {
label: "Inter Tight 700",
family: "'Inter Tight', Inter, system-ui, sans-serif",
weight: 700, size1: 0.46, size2: 0.34, ls1: "-0.03em", ls2: "-0.05em",
},
};
// ----------------------------------------------------------------------
// FrameShape — a single filled rounded-rect frame with a top-left notch.
// Exposes gradients/filters so both Umbrella and Module variants can reuse.
// ----------------------------------------------------------------------
function FrameShape({
size = 240,
pad = 10,
thickness = 28,
ro = 24,
ri = 10,
gap = 18,
gapCenterFrac = 0.28,
children,
idPrefix = "f",
}) {
const x0 = pad, y0 = pad;
const x1 = size - pad, y1 = size - pad;
const ix0 = x0 + thickness, iy0 = y0 + thickness;
const ix1 = x1 - thickness, iy1 = y1 - thickness;
const cx = size / 2;
const outer = `M ${x0 + ro} ${y0}
H ${x1 - ro} A ${ro} ${ro} 0 0 1 ${x1} ${y0 + ro}
V ${y1 - ro} A ${ro} ${ro} 0 0 1 ${x1 - ro} ${y1}
H ${x0 + ro} A ${ro} ${ro} 0 0 1 ${x0} ${y1 - ro}
V ${y0 + ro} A ${ro} ${ro} 0 0 1 ${x0 + ro} ${y0} Z`;
const inner = `M ${ix0 + ri} ${iy0}
H ${ix1 - ri} A ${ri} ${ri} 0 0 1 ${ix1} ${iy0 + ri}
V ${iy1 - ri} A ${ri} ${ri} 0 0 1 ${ix1 - ri} ${iy1}
H ${ix0 + ri} A ${ri} ${ri} 0 0 1 ${ix0} ${iy1 - ri}
V ${iy0 + ri} A ${ri} ${ri} 0 0 1 ${ix0 + ri} ${iy0} Z`;
const notchCx = x0 + (x1 - x0) * gapCenterFrac;
const notchX0 = notchCx - gap / 2;
const notchX1 = notchCx + gap / 2;
return {
cx, size, pad, thickness, x0, y0, x1, y1, ix0, iy0, ix1, iy1,
outer, inner, notchX0, notchX1, notchY0: y0 - 4, notchY1: iy0 + 4,
idPrefix,
};
}
// ----------------------------------------------------------------------
// UmbrellaLogo — the main SloppyUniverse mark.
// 4-color frame (one color per quadrant), subtle gradient + inner highlight
// for plastic depth, white UNIV/ERSE with dark outline for legibility.
// ----------------------------------------------------------------------
function UmbrellaLogo({
size = 240,
colors = {
tl: SU_COLORS.green,
tr: SU_COLORS.navy,
br: SU_COLORS.orange,
bl: SU_COLORS.magenta,
},
idPrefix = "umb",
plastic = true,
onDark = false,
textColor = SU_COLORS.paper,
outlineColor = SU_COLORS.outline,
}) {
const s = size;
// Everything scales from size (defaults tuned for 240).
const k = s / 240;
const pad = 10 * k;
const thickness = 28 * k;
const ro = 24 * k;
const ri = 10 * k;
const gap = 18 * k;
const gapCenterFrac = 0.28;
const outlineWidth = 3 * k;
const rowHeight = 54 * k;
const lineGap = 4 * k;
const wordInset = 36 * k;
const g = FrameShape({ size: s, pad, thickness, ro, ri, gap, gapCenterFrac, idPrefix });
const maskId = `${idPrefix}-mask`;
const hiId = `${idPrefix}-hi`;
const haloId = `${idPrefix}-halo`;
return (
);
}
function UmbrellaWordmark({
cx, cy, width, rowHeight, lineGap,
color, outlineColor, outlineWidth,
fontFamily = "'Archivo Black', 'Arial Black', system-ui, sans-serif",
}) {
const cap = rowHeight * 0.72;
const topY = cy - (cap + lineGap) / 2;
const bottomY = cy + (cap + lineGap) / 2;
const base = {
textAnchor: "middle",
fontFamily,
fontWeight: 900,
fontSize: rowHeight,
textLength: width,
lengthAdjust: "spacingAndGlyphs",
dominantBaseline: "middle",
paintOrder: "stroke fill",
stroke: outlineColor,
strokeWidth: outlineWidth,
strokeLinejoin: "round",
};
return (
UNIV
ERSE
);
}
// ----------------------------------------------------------------------
// ModuleIcon — same frame shape, monochrome color, single big letter.
// ----------------------------------------------------------------------
function ModuleIcon({
moduleKey = "fix",
size = 240,
idPrefix,
plastic = true,
glyph,
font = "inter-tight", // key into FONT_PRESETS
}) {
const m = SU_MODULES[moduleKey];
const label = glyph ?? m.letter;
const labelLen = label.length;
const twoChar = labelLen >= 2;
const preset = FONT_PRESETS[font] || FONT_PRESETS["space-grotesk"];
// Fuer 3-Zeichen-Wörter (fix, pay) ~70% des 2-letter Sizes,
// fuer 4-Zeichen-Wörter (scan, plan, rent) ~55%. Sonst werden die
// Buchstaben breiter als der Frame.
const lenScale = labelLen <= 2 ? 1.0 : (labelLen === 3 ? 0.70 : 0.55);
const s = size;
const prefix = idPrefix || `mod-${moduleKey}`;
const pad = Math.round(s * 0.06);
const thickness = Math.round(s * 0.11);
const ro = Math.round(s * 0.14);
const ri = Math.round(s * 0.05);
const gap = Math.round(s * 0.07);
const g = FrameShape({ size: s, pad, thickness, ro, ri, gap, gapCenterFrac: 0.28, idPrefix: prefix });
const maskId = `${prefix}-mask`;
const hiId = `${prefix}-hi`;
// Darker inner panel behind the letter
const ix0 = g.ix0, iy0 = g.iy0, ix1 = g.ix1, iy1 = g.iy1;
const innerR = ri;
return (
);
}
// ----------------------------------------------------------------------
// UmbrellaMono — single color variant (for print, stamping, dark BG)
// ----------------------------------------------------------------------
function UmbrellaMono({ size = 240, color = SU_COLORS.ink, idPrefix = "mono" }) {
return (
);
}
// ----------------------------------------------------------------------
// UmbrellaLockup — horizontal mark + wordmark
// ----------------------------------------------------------------------
function UmbrellaLockup({ size = 380 }) {
const h = 120;
const w = 420;
return (
);
}
Object.assign(window, {
UmbrellaLogo, UmbrellaMono, UmbrellaLockup, ModuleIcon,
SU_COLORS, SU_MODULES, FONT_PRESETS,
// Legacy aliases
LogoA: UmbrellaLogo,
LogoB: (props) => ,
LogoC: UmbrellaLogo,
LogoD: UmbrellaLogo,
LogoE: UmbrellaLockup,
LogoF: UmbrellaMono,
LogoPrimary: UmbrellaLogo,
LogoThin: (props) => ,
LogoTile: UmbrellaLogo,
LogoMono: UmbrellaMono,
LogoLockup: UmbrellaLockup,
});