diff --git a/frontend/libs/components/video-editor/src/lib/panels/properties/hooks/use-property-draft.spec.tsx b/frontend/libs/components/video-editor/src/lib/panels/properties/hooks/use-property-draft.spec.tsx
new file mode 100644
--- /dev/null
+++ b/frontend/libs/components/video-editor/src/lib/panels/properties/hooks/use-property-draft.spec.tsx
@@ -0,0 +1,31 @@
+import { act, createElement } from "react";
+import { createRoot } from "react-dom/client";
+import { usePropertyDraft } from "./use-property-draft";
+import { usePropertyDraft as useAssetPropertyDraft } from "../../assets/hooks/use-property-draft";
+
+it.each([usePropertyDraft, useAssetPropertyDraft])("previews a leading-decimal calculation on blur before committing", (useDraft) => {
+  vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
+  const events: unknown[] = [];
+  let draft!: ReturnType<typeof usePropertyDraft<number>>;
+  function Probe() {
+    draft = useDraft({
+      displayValue: "100",
+      parse: (text: string) => text.trim() && Number.isFinite(Number(text)) ? Number(text) : null,
+      onPreview: (value: number) => events.push(value),
+      onCommit: () => events.push("commit"),
+    });
+    return null;
+  }
+  const root = createRoot(document.createElement("div"));
+  try {
+    act(() => root.render(createElement(Probe)));
+    act(() => draft.onFocus());
+    act(() => draft.onChange({ target: { value: ".5 * 100" } } as Parameters<typeof draft.onChange>[0]));
+    act(() => draft.onBlur({ target: { value: ".5 * 100" } } as Parameters<typeof draft.onBlur>[0]));
+    expect(events).toEqual([50, "commit"]);
+    expect(draft!.displayValue).toBe("100");
+  } finally {
+    act(() => root.unmount());
+    vi.unstubAllGlobals();
+  }
+});
diff --git a/frontend/libs/components/video-editor/src/lib/utils/math.spec.ts b/frontend/libs/components/video-editor/src/lib/utils/math.spec.ts
new file mode 100644
--- /dev/null
+++ b/frontend/libs/components/video-editor/src/lib/utils/math.spec.ts
@@ -0,0 +1,20 @@
+import { evaluateMathExpression } from "./math";
+
+describe("property arithmetic decimal operands", () => {
+  it.each([
+    [".5 * 100", 50],
+    ["2 + .25", 2.25],
+    ["(-.5 + 1) * 40", 20],
+    [".5 / .25", 2],
+  ])("evaluates %s", (input, result) => {
+    expect(evaluateMathExpression({ input: String(input) })).toBe(result);
+  });
+
+  it.each([".", "1..2", ".5.5", "1 + .", "2 / 0", "1 +", "2 3", "alert(1)"])("rejects malformed expression %s", (input) => {
+    expect(evaluateMathExpression({ input: String(input) })).toBeNull();
+  });
+
+  it.each([["2 * (3 + 4)", 14], ["-2 + 3", 1], ["0.5 * 100", 50], ["1. + 2", 3]])("preserves %s", (input, result) => {
+    expect(evaluateMathExpression({ input: String(input) })).toBe(result);
+  });
+});
diff --git a/frontend/libs/components/video-editor/src/lib/utils/math.ts b/frontend/libs/components/video-editor/src/lib/utils/math.ts
--- a/frontend/libs/components/video-editor/src/lib/utils/math.ts
+++ b/frontend/libs/components/video-editor/src/lib/utils/math.ts
@@ -228,7 +228,7 @@ function tokenize(input: string): Token[] {
       continue;
     }
 
-    if (/\d/.test(char)) {
+    if (/\d/.test(char) || char === ".") {
       let numStr = "";
       while (i < str.length && (/\d/.test(str[i]) || str[i] === ".")) {
         numStr += str[i];
