diff --git a/src/admin/components/odin/OdinMark.tsx b/src/admin/components/odin/OdinMark.tsx
new file mode 100644
index 0000000..0db8d6c
--- /dev/null
+++ b/src/admin/components/odin/OdinMark.tsx
@@ -0,0 +1,117 @@
+import { keyframes, styled } from "@mui/material/styles";
+import Box from "@mui/material/Box";
+import { useReducedMotion } from "framer-motion";
+
+/**
+ * Odin's animated brand mark: an AI "spark" (Odin) with a data node orbiting a
+ * ring — the assistant at the centre of the business system, in BOHA red. The
+ * spark slow-rotates and breathes; the node orbits. In the "thinking" state
+ * everything speeds up and the tile glows. Honours prefers-reduced-motion.
+ *
+ * The animations live in a `styled("svg")` (not inline style): Emotion only
+ * injects the @keyframes rule when the keyframes object is used inside
+ * sx/css/styled, and the child selectors keep the SVG elements plain/typed.
+ */
+const breathe = keyframes`
+ 0%, 100% { transform: scale(0.9); opacity: 0.9; }
+ 50% { transform: scale(1.08); opacity: 1; }
+`;
+const spin = keyframes`
+ from { transform: rotate(0deg); }
+ to { transform: rotate(360deg); }
+`;
+// Reduce-motion safe: opacity only (no transforms / no travel).
+const glow = keyframes`
+ 0%, 100% { opacity: 0.55; }
+ 50% { opacity: 1; }
+`;
+
+const Glyph = styled("svg", {
+ shouldForwardProp: (p) => p !== "thinking" && p !== "reduce",
+})<{ thinking: boolean; reduce: boolean }>(({ thinking, reduce }) => ({
+ width: "70%",
+ height: "70%",
+ overflow: "visible",
+ display: "block",
+ ...(reduce
+ ? {
+ // Reduce-motion: keep a calm opacity glow (no spin/orbit/travel). The
+ // `!important` + class specificity intentionally overrides the app-wide
+ // reduced-motion reset in GlobalStyles for this one small mark, since an
+ // opacity pulse is accessibility-safe (it isn't vestibular motion).
+ "& .odin-spark": {
+ animation: `${glow} ${thinking ? "1.1s" : "3s"} ease-in-out infinite !important`,
+ },
+ }
+ : {
+ "& .odin-orbit": {
+ transformBox: "view-box",
+ transformOrigin: "12px 12px",
+ animation: `${spin} ${thinking ? "1.6s" : "5.5s"} linear infinite`,
+ },
+ "& .odin-spin": {
+ transformBox: "view-box",
+ transformOrigin: "12px 12px",
+ animation: `${spin} ${thinking ? "4s" : "11s"} linear infinite`,
+ },
+ "& .odin-spark": {
+ transformBox: "fill-box",
+ transformOrigin: "center",
+ animation: `${breathe} ${thinking ? "1s" : "2.8s"} ease-in-out infinite`,
+ },
+ }),
+}));
+
+export interface OdinMarkProps {
+ size?: number;
+ state?: "idle" | "thinking";
+}
+
+export default function OdinMark({ size = 28, state = "idle" }: OdinMarkProps) {
+ const reduce = !!useReducedMotion();
+ const thinking = state === "thinking";
+
+ return (
+ ({
+ width: size,
+ height: size,
+ borderRadius: "32%",
+ flexShrink: 0,
+ display: "inline-flex",
+ alignItems: "center",
+ justifyContent: "center",
+ background: `linear-gradient(135deg, ${t.vars!.palette.primary.main}, ${t.vars!.palette.primary.dark})`,
+ boxShadow: thinking
+ ? `0 0 ${Math.round(size * 0.5)}px 0 rgba(${t.vars!.palette.primary.mainChannel} / 0.55)`
+ : "none",
+ transition: "box-shadow 0.3s ease",
+ })}
+ >
+
+ {/* orbit ring */}
+
+ {/* orbiting data node */}
+
+
+
+ {/* spark — group slow-rotates, the star breathes about its own centre */}
+
+
+
+
+
+ );
+}
diff --git a/src/admin/components/odin/OdinSidebar.tsx b/src/admin/components/odin/OdinSidebar.tsx
index 67d6448..8268b05 100644
--- a/src/admin/components/odin/OdinSidebar.tsx
+++ b/src/admin/components/odin/OdinSidebar.tsx
@@ -5,6 +5,7 @@ import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem";
import IconButton from "@mui/material/IconButton";
import { Button, TextField, Modal, ConfirmDialog } from "../../ui";
+import OdinMark from "./OdinMark";
interface OdinSidebarProps {
conversations: { id: number; title: string }[];
@@ -78,20 +79,7 @@ export default function OdinSidebar({
{/* Brand + New chat */}
-
- AI
-
+
Odin
diff --git a/src/admin/components/odin/OdinThread.tsx b/src/admin/components/odin/OdinThread.tsx
index d80a2ef..4d6b8c9 100644
--- a/src/admin/components/odin/OdinThread.tsx
+++ b/src/admin/components/odin/OdinThread.tsx
@@ -1,7 +1,7 @@
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
-import CircularProgress from "@mui/material/CircularProgress";
import type { ChatTurn } from "./types";
+import OdinMark from "./OdinMark";
interface OdinThreadProps {
turns: ChatTurn[];
@@ -84,7 +84,7 @@ export default function OdinThread({
px: 2,
}}
>
-
+
Dobrý den, jsem Odin. Zeptejte se, nebo přiložte fakturu (PDF) k
importu.
@@ -147,7 +147,7 @@ export default function OdinThread({
color: "text.secondary",
}}
>
-
+
Pracuji…