/* ====================================================================== KOREALM — UI primitives + icons ====================================================================== */ /* ---------------- Icons (feather-style, 24 grid) ---------------- */ const ICONS = { home: "M3 11.5 12 4l9 7.5M5 10v10h14V10", search: "M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM21 21l-4.3-4.3", filter: "M3 5h18M6 12h12M10 19h4", bed: "M3 8v10M3 13h18v5M21 18v-4a3 3 0 0 0-3-3H9a3 3 0 0 0-3 3M3 8h2", bath: "M4 12h16v3a4 4 0 0 1-4 4H8a4 4 0 0 1-4-4v-3ZM7 12V6a2 2 0 0 1 4 0M6 19l-1 2M18 19l1 2", ruler: "M3 7h18v10H3zM7 7v3M11 7v5M15 7v3M19 7v5", pin: "M12 21s7-6.5 7-12a7 7 0 1 0-14 0c0 5.5 7 12 7 12ZM12 11.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5Z", heart: "M12 20s-7-4.4-9.2-8.6C1.2 8.1 3 5 6 5c1.9 0 3.2 1.1 4 2.3C10.8 6.1 12.1 5 14 5c3 0 4.8 3.1 3.2 6.4C19 15.6 12 20 12 20Z", check: "M5 12.5 10 17l9-10", checkCircle: "M22 11.1V12a10 10 0 1 1-5.9-9.1M22 4 12 14.1l-3-3", x: "M6 6l12 12M18 6 6 18", xCircle: "M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20ZM15 9l-6 6M9 9l6 6", clock: "M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20ZM12 7v5l3 2", calendar: "M7 3v3M17 3v3M3.5 9h17M5 5h14a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1Z", user: "M12 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8ZM5 20c0-3.3 3.1-6 7-6s7 2.7 7 6", users: "M9 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8ZM2 20c0-3.3 3-6 7-6s7 2.7 7 6M16 4.5a4 4 0 0 1 0 7.5M22 20c0-2.6-1.6-4.6-4-5.5", building: "M4 21V5a1 1 0 0 1 1-1h9a1 1 0 0 1 1 1v16M15 21V9h4a1 1 0 0 1 1 1v11M8 8h3M8 12h3M8 16h3M3 21h18", card: "M3 7h18a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1ZM2 10h20M6 14h4", wallet: "M3 7h15a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h12M17 12.5h.01M16 12.5a1 1 0 1 0 2 0 1 1 0 0 0-2 0Z", doc: "M14 3v5h5M7 3h8l5 5v12a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1ZM9 13h6M9 17h6", shield: "M12 3l8 3v6c0 5-3.5 8-8 9-4.5-1-8-4-8-9V6l8-3ZM9 12l2 2 4-4", upload: "M12 16V4M7 9l5-5 5 5M5 20h14", wrench: "M14.5 6.5a3.5 3.5 0 0 0 4.6 4.6L21 13l-8 8-2-2-5.5-5.5a3.5 3.5 0 0 1 4.6-4.6L14.5 6.5ZM14.5 6.5 18 3", grid: "M4 4h7v7H4zM13 4h7v7h-7zM4 13h7v7H4zM13 13h7v7h-7z", chart: "M4 20V4M4 20h16M8 16v-4M12 16V8M16 16v-7", bell: "M6 9a6 6 0 1 1 12 0c0 5 2 6 2 6H4s2-1 2-6ZM10 19a2 2 0 0 0 4 0", logout: "M14 8V6a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2M9 12h11M17 9l3 3-3 3", chevR: "M9 5l7 7-7 7", chevL: "M15 5l-7 7 7 7", chevD: "M6 9l6 6 6-6", arrowR: "M5 12h14M13 6l6 6-6 6", arrowL: "M19 12H5M11 6l-6 6 6 6", plus: "M12 5v14M5 12h14", minus: "M5 12h14", dots: "M5 12h.01M12 12h.01M19 12h.01", star: "M12 3l2.6 5.5 6 .8-4.3 4.2 1 6L12 16.9 6.7 19.5l1-6L3.4 9.3l6-.8L12 3Z", mail: "M3 6h18a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1ZM3 7l9 6 9-6", phone: "M5 4h3l2 5-2 1.5a11 11 0 0 0 5 5L16 13l5 2v3a2 2 0 0 1-2 2A16 16 0 0 1 3 6a2 2 0 0 1 2-2Z", lock: "M6 11h12a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1v-7a1 1 0 0 1 1-1ZM8 11V8a4 4 0 0 1 8 0v3", eye: "M2 12s4-7 10-7 10 7 10 7-4 7-10 7-10-7-10-7ZM12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z", key: "M14 7a4 4 0 1 1-5.7 5.7L4 17v3h3l1-1h2v-2h2l1.3-1.3A4 4 0 0 1 14 7ZM16 9h.01", sparkle: "M12 3l1.8 5L19 9.8 13.8 11.6 12 17l-1.8-5.4L5 9.8l5.2-1.8L12 3Z", trend: "M3 17l6-6 4 4 7-8M21 7h-4M21 7v4", download: "M12 4v12M7 11l5 5 5-5M5 20h14", pause: "M9 5v14M15 5v14", play: "M7 4v16l13-8-13-8Z", refresh: "M21 12a9 9 0 1 1-2.6-6.4M21 4v5h-5", info: "M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20ZM12 11v5M12 8h.01", alert: "M12 9v4M12 17h.01M10.3 3.9 2.4 18a2 2 0 0 0 1.7 3h15.8a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z", menu: "M4 7h16M4 12h16M4 17h16", pencil: "M14 5l5 5M4 20l1-4 11-11 4 4-11 11-4 1Z", map: "M9 4 3 6v14l6-2 6 2 6-2V4l-6 2-6-2ZM9 4v14M15 6v14", trash: "M5 7h14M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2M7 7l1 13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1l1-13", send: "M4 12 20 4l-6 16-3-7-7-1Z", hash: "M9 3 7 21M17 3l-2 18M4 8h16M3 16h16", briefcase: "M4 8h16a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1ZM9 8V6a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2M3 13h18", paw: "M11 14c-2 0-4 1.5-4 3.5S9 20 11 20h2c2 0 4-.5 4-2.5S15 14 13 14ZM6 10a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM18 10a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM9.5 8a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM14.5 8a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z", gitBranch: "M6 3v12M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM15 6a9 9 0 0 1-9 9", route: "M6 19a2 2 0 1 0 0-4 2 2 0 0 0 0 4ZM18 9a2 2 0 1 0 0-4 2 2 0 0 0 0 4ZM8 17h6.5a3.5 3.5 0 0 0 0-7H9.5a3.5 3.5 0 0 1 0-7H16", node: "M12 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4ZM12 2v8M12 14v8M2 12h8M14 12h8", flame: "M12 2c1 4 5 5 5 9a5 5 0 0 1-10 0c0-2 1-3 2-4 0 2 1 3 2 3 0-3-1-5 1-8Z", zap: "M13 2 4 14h7l-1 8 9-12h-7z", leaf: "M4 20C4 11 11 4 20 4c0 9-7 16-16 16ZM4 20c3-5 7-8 12-9", tag: "M3 11V4a1 1 0 0 1 1-1h7l9 9-8 8-9-9ZM7.5 7.5h.01", layers: "M12 3 3 8l9 5 9-5-9-5ZM3 12l9 5 9-5M3 16l9 5 9-5", receipt: "M5 3h14v18l-3-2-2 2-2-2-2 2-2-2-3 2V3ZM9 8h6M9 12h6M9 16h4", }; function Icon({ name, size = 18, className = "", style, strokeWidth = 1.9 }) { const d = ICONS[name]; if (!d) return null; return ( {d.split("M").filter(Boolean).map((seg, i) => )} ); } /* ---------------- Button ---------------- */ function Button({ variant = "primary", size, icon, iconR, children, className = "", ...p }) { const cls = ["btn", "btn-" + variant, size && "btn-" + size, !children && "btn-icon", className].filter(Boolean).join(" "); return ( ); } /* ---------------- Badge ---------------- */ function Badge({ tone = "neutral", dot, children, className = "" }) { return {dot && }{children}; } /* status → tone+label maps */ const STATUS = { available: ["success", "Available"], under_application: ["warning", "Under application"], leased: ["neutral", "Leased"], requested: ["warning", "Requested"], confirmed: ["info", "Confirmed"], completed: ["success", "Completed"], declined: ["danger", "Declined"], pending: ["warning", "Pending"], approved: ["success", "Approved"], rejected: ["danger", "Rejected"], paid: ["success", "Paid"], failed: ["danger", "Failed"], not_started: ["neutral", "Not started"], submitted: ["warning", "Pending review"], verified: ["success", "Verified"], open: ["warning", "Open"], assigned: ["info", "Assigned"], in_progress: ["info", "In progress"], resolved: ["success", "Resolved"], }; function StatusBadge({ status, dot = true }) { const [tone, label] = STATUS[status] || ["neutral", status]; return {label}; } const PRIORITY = { high: ["danger", "High"], medium: ["warning", "Medium"], low: ["neutral", "Low"] }; function PriorityBadge({ p }) { const [t, l] = PRIORITY[p] || ["neutral", p]; return {l}; } /* ---------------- Avatar ---------------- */ function Avatar({ name, size = 36, square, tone, src }) { const st = { width: size, height: size, fontSize: size * 0.38 }; if (tone) { st.background = tone.bg; st.color = tone.fg; } return {window.fmt.initials(name)}; } /* ---------------- Field / inputs ---------------- */ function Field({ label, req, hint, error, children, className = "" }) { return (
{label && } {children} {error ? {error} : hint ? {hint} : null}
); } function Input({ error, icon, ...p }) { const el = ; if (icon) return
{el}
; return el; } function MoneyInput({ error, ...p }) { return
£
; } function Select({ error, children, ...p }) { return ; } function Textarea({ error, ...p }) { return