diff --git a/README.md b/README.md
index ccce63c..8ab7a7b 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
# Web Video Optimizer
-
-
+
+
[](https://github.com/Artsen/web-video-optimizer/actions/workflows/ci.yml)
diff --git a/apps/web/index.html b/apps/web/index.html
index 644b148..fd783e5 100644
--- a/apps/web/index.html
+++ b/apps/web/index.html
@@ -3,7 +3,7 @@
-
+
Local Web Video Optimizer
diff --git a/apps/web/public/brand/WVO-favicon.png b/apps/web/public/brand/WVO-favicon.png
new file mode 100644
index 0000000..20bba6b
Binary files /dev/null and b/apps/web/public/brand/WVO-favicon.png differ
diff --git a/apps/web/public/brand/WVO-logo-transparent.png b/apps/web/public/brand/WVO-logo-transparent.png
new file mode 100644
index 0000000..537b6a0
Binary files /dev/null and b/apps/web/public/brand/WVO-logo-transparent.png differ
diff --git a/apps/web/public/brand/WVO-logo-white-transparent.png b/apps/web/public/brand/WVO-logo-white-transparent.png
new file mode 100644
index 0000000..8dc86d8
Binary files /dev/null and b/apps/web/public/brand/WVO-logo-white-transparent.png differ
diff --git a/apps/web/public/favicon.svg b/apps/web/public/favicon.svg
deleted file mode 100644
index 4492fc6..0000000
--- a/apps/web/public/favicon.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-
diff --git a/apps/web/src/app/App.test.tsx b/apps/web/src/app/App.test.tsx
index 4468123..7b1286d 100644
--- a/apps/web/src/app/App.test.tsx
+++ b/apps/web/src/app/App.test.tsx
@@ -132,7 +132,7 @@ describe("App behavior", () => {
renderApp(api);
expect(await screen.findByRole("heading", { name: "Ready for a source video" })).toBeInTheDocument();
- const warning = screen.getByRole("status");
+ const warning = await screen.findByRole("status");
expect(warning).toHaveTextContent("Storage status");
expect(warning).not.toHaveTextContent("D:\\private");
diff --git a/apps/web/src/assets/brand/mark-monochrome.svg b/apps/web/src/assets/brand/mark-monochrome.svg
deleted file mode 100644
index 41a9a19..0000000
--- a/apps/web/src/assets/brand/mark-monochrome.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/apps/web/src/assets/brand/mark.svg b/apps/web/src/assets/brand/mark.svg
deleted file mode 100644
index e4ce861..0000000
--- a/apps/web/src/assets/brand/mark.svg
+++ /dev/null
@@ -1,14 +0,0 @@
-
diff --git a/apps/web/src/assets/brand/wordmark-dark.svg b/apps/web/src/assets/brand/wordmark-dark.svg
deleted file mode 100644
index 712501c..0000000
--- a/apps/web/src/assets/brand/wordmark-dark.svg
+++ /dev/null
@@ -1,16 +0,0 @@
-
diff --git a/apps/web/src/assets/brand/wordmark-light.svg b/apps/web/src/assets/brand/wordmark-light.svg
deleted file mode 100644
index 8855109..0000000
--- a/apps/web/src/assets/brand/wordmark-light.svg
+++ /dev/null
@@ -1,16 +0,0 @@
-
diff --git a/apps/web/src/components/ui/AppMark.test.tsx b/apps/web/src/components/ui/AppMark.test.tsx
index b97ede2..7294694 100644
--- a/apps/web/src/components/ui/AppMark.test.tsx
+++ b/apps/web/src/components/ui/AppMark.test.tsx
@@ -1,21 +1,29 @@
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
-import { AppMark } from "./AppMark";
+import { AppMark, WVO_LOGO_TRANSPARENT_PATH, WVO_LOGO_WHITE_TRANSPARENT_PATH } from "./AppMark";
describe("AppMark", () => {
- it("renders the compression-frame brand mark as decorative SVG geometry", () => {
+ it("renders the approved WVO monogram variants as decorative images", () => {
render(
-
+ Web Video Optimizer
);
const mark = document.querySelector(".app-mark");
expect(mark).toHaveAttribute("aria-hidden", "true");
+ expect(mark).toHaveClass("small");
expect(screen.getByText("Web Video Optimizer")).toBeVisible();
- expect(document.querySelector(".app-mark-frame")).toBeInTheDocument();
- expect(document.querySelectorAll(".app-mark-band")).toHaveLength(3);
- expect(document.querySelector(".app-mark-play")).toBeInTheDocument();
+
+ const images = document.querySelectorAll(".app-mark-image");
+ expect(images).toHaveLength(2);
+ expect(images[0]).toHaveAttribute("src", WVO_LOGO_TRANSPARENT_PATH);
+ expect(images[1]).toHaveAttribute("src", WVO_LOGO_WHITE_TRANSPARENT_PATH);
+ expect(images[0]).toHaveAttribute("alt", "");
+ expect(images[1]).toHaveAttribute("alt", "");
+ expect(images[0]).toHaveAttribute("draggable", "false");
+ expect(images[1]).toHaveAttribute("draggable", "false");
+ expect(mark?.querySelector("svg")).not.toBeInTheDocument();
});
});
diff --git a/apps/web/src/components/ui/AppMark.tsx b/apps/web/src/components/ui/AppMark.tsx
index c1af220..347afaf 100644
--- a/apps/web/src/components/ui/AppMark.tsx
+++ b/apps/web/src/components/ui/AppMark.tsx
@@ -1,23 +1,16 @@
+export const WVO_LOGO_TRANSPARENT_PATH = "/brand/WVO-logo-transparent.png";
+export const WVO_LOGO_WHITE_TRANSPARENT_PATH = "/brand/WVO-logo-white-transparent.png";
+
export function AppMark({ size = "default" }: { size?: "default" | "small" }) {
return (
-
-
+
+
+
);
}
diff --git a/apps/web/src/components/ui/design-primitives.test.tsx b/apps/web/src/components/ui/design-primitives.test.tsx
index eef0d8d..e8a4bbb 100644
--- a/apps/web/src/components/ui/design-primitives.test.tsx
+++ b/apps/web/src/components/ui/design-primitives.test.tsx
@@ -9,6 +9,7 @@ describe("design primitives", () => {
const { container } = render();
expect(container.querySelector(".app-mark")).toHaveAttribute("aria-hidden", "true");
+ expect(container.querySelector(".app-mark")).toHaveClass("default");
});
it("renders status badges with semantic text and tone classes", () => {
diff --git a/apps/web/src/styles/components.css b/apps/web/src/styles/components.css
index 6e6bf55..e9b63a8 100644
--- a/apps/web/src/styles/components.css
+++ b/apps/web/src/styles/components.css
@@ -1,4 +1,3 @@
-.app-mark,
.brand-mark,
.upload-icon,
.preset-icon {
@@ -12,40 +11,31 @@
}
.app-mark {
- width: 40px;
- height: 40px;
+ display: block;
+ width: 86px;
+ height: auto;
flex: 0 0 auto;
- padding: 3px;
- overflow: hidden;
- background:
- linear-gradient(var(--color-surface-recessed), var(--color-surface-recessed)) padding-box,
- var(--gradient-brand-spectral) border-box;
- border-color: transparent;
+ line-height: 0;
}
.app-mark.small {
- width: 32px;
- height: 32px;
- padding: 2px;
+ width: 58px;
}
-.app-mark-icon {
- display: block;
+.app-mark-image {
+ display: none;
width: 100%;
- height: 100%;
-}
-
-.app-mark-frame {
- fill: url("#app-mark-spectral");
+ height: auto;
+ object-fit: contain;
+ user-select: none;
}
-.app-mark-band {
- fill: #ffffff;
- fill-opacity: 0.92;
+:root:not([data-theme="light"]) .app-mark-image-white {
+ display: block;
}
-.app-mark-play {
- fill: var(--color-ember-text);
+:root[data-theme="light"] .app-mark-image-dark {
+ display: block;
}
.upload-icon {
@@ -1199,8 +1189,7 @@ progress::-moz-progress-bar {
}
.source-topbar .app-mark {
- width: 34px;
- height: 34px;
+ width: 76px;
}
.source-topbar h1 {
@@ -2663,7 +2652,6 @@ progress::-moz-progress-bar {
}
@media (forced-colors: active) {
- .app-mark,
.upload-icon {
forced-color-adjust: auto;
color: CanvasText;
@@ -2671,10 +2659,8 @@ progress::-moz-progress-bar {
border-color: ButtonBorder;
}
- .app-mark-frame,
- .app-mark-band,
- .app-mark-play {
- fill: currentColor;
+ .app-mark-image {
+ forced-color-adjust: auto;
}
.dropzone.dropzone-empty::before,
diff --git a/apps/web/src/styles/layout.css b/apps/web/src/styles/layout.css
index 325729e..02d5a81 100644
--- a/apps/web/src/styles/layout.css
+++ b/apps/web/src/styles/layout.css
@@ -115,9 +115,7 @@
}
.topbar .app-mark {
- width: 34px;
- height: 34px;
- border-radius: var(--radius-card);
+ width: 86px;
}
.topbar h1 {
diff --git a/docs/assets/brand/mark.svg b/docs/assets/brand/mark.svg
deleted file mode 100644
index e4ce861..0000000
--- a/docs/assets/brand/mark.svg
+++ /dev/null
@@ -1,14 +0,0 @@
-
diff --git a/docs/assets/brand/wordmark-dark.svg b/docs/assets/brand/wordmark-dark.svg
deleted file mode 100644
index 712501c..0000000
--- a/docs/assets/brand/wordmark-dark.svg
+++ /dev/null
@@ -1,16 +0,0 @@
-
diff --git a/docs/assets/brand/wordmark-light.svg b/docs/assets/brand/wordmark-light.svg
deleted file mode 100644
index 8855109..0000000
--- a/docs/assets/brand/wordmark-light.svg
+++ /dev/null
@@ -1,16 +0,0 @@
-
diff --git a/docs/assets/screenshots/README.md b/docs/assets/screenshots/README.md
index 3ab01b5..e574caa 100644
--- a/docs/assets/screenshots/README.md
+++ b/docs/assets/screenshots/README.md
@@ -25,4 +25,4 @@ Review captures are full-page unless the Playwright screenshot suite names them
The current curated set balances dark workflow coverage, one light-theme result, Compare theatre, Custom Export density, and mobile bottom chrome. Add an empty-state screenshot only when it materially improves the README.
-Current curated set: 6 WebP files, 260,638 bytes total.
+Current curated set: 6 WebP files, 333,348 bytes total.
diff --git a/docs/assets/screenshots/compare-wipe-dark.webp b/docs/assets/screenshots/compare-wipe-dark.webp
index a8be3bf..48fe56f 100644
Binary files a/docs/assets/screenshots/compare-wipe-dark.webp and b/docs/assets/screenshots/compare-wipe-dark.webp differ
diff --git a/docs/assets/screenshots/custom-export-dark.webp b/docs/assets/screenshots/custom-export-dark.webp
index b8e30b6..1264efd 100644
Binary files a/docs/assets/screenshots/custom-export-dark.webp and b/docs/assets/screenshots/custom-export-dark.webp differ
diff --git a/docs/assets/screenshots/mobile-results-dark.webp b/docs/assets/screenshots/mobile-results-dark.webp
index 9f67d81..7dfea5b 100644
Binary files a/docs/assets/screenshots/mobile-results-dark.webp and b/docs/assets/screenshots/mobile-results-dark.webp differ
diff --git a/docs/assets/screenshots/prepare-dark.webp b/docs/assets/screenshots/prepare-dark.webp
index 70e57bc..ef7eb1c 100644
Binary files a/docs/assets/screenshots/prepare-dark.webp and b/docs/assets/screenshots/prepare-dark.webp differ
diff --git a/docs/assets/screenshots/results-dark.webp b/docs/assets/screenshots/results-dark.webp
index 803f076..2b9bfab 100644
Binary files a/docs/assets/screenshots/results-dark.webp and b/docs/assets/screenshots/results-dark.webp differ
diff --git a/docs/assets/screenshots/results-light.webp b/docs/assets/screenshots/results-light.webp
index d6b3995..e4e8664 100644
Binary files a/docs/assets/screenshots/results-light.webp and b/docs/assets/screenshots/results-light.webp differ
diff --git a/docs/brand-system.md b/docs/brand-system.md
index c9acb18..a5aacf3 100644
--- a/docs/brand-system.md
+++ b/docs/brand-system.md
@@ -15,23 +15,21 @@ The intended balance is roughly 80% slate structure, 15% tonal elevation and bor
## Logo
-The mark is a compression-frame symbol: an outer video frame, three narrowing source bands, and a terminal output/play shape. It should suggest media reduction and website-ready delivery without resembling a generic clapperboard or AI sparkle.
+The approved mark is a fused WVO monogram. The W and V share overlapping geometry, while the O acts as the terminal output form. The chromatic cyan and ember edge separation is part of the artwork and should remain intact.
-Canonical app assets live in `apps/web/src/assets/brand/`. Documentation copies live in `docs/assets/brand/` so GitHub can render README imagery.
+Canonical app and documentation assets live in `apps/web/public/brand/` so the Vite app, favicon, README, and packaged static paths use the same source files.
Logo variants:
-- `mark.svg`: spectral icon mark.
-- `mark-monochrome.svg`: one-color app mark.
-- `wordmark-dark.svg`: horizontal logo for dark backgrounds.
-- `wordmark-light.svg`: horizontal logo for light backgrounds.
-- `apps/web/public/favicon.svg`: favicon-compatible mark.
+- `WVO-logo-transparent.png`: dark letterform variant for light surfaces.
+- `WVO-logo-white-transparent.png`: white letterform variant for dark surfaces.
+- `WVO-favicon.png`: favicon derived from the approved monogram for small browser chrome.
When the mark sits next to visible "Web Video Optimizer" text, it should be `aria-hidden` to avoid duplicate announcements. Use text alternatives for standalone documentation imagery.
-Clear space should be at least one quarter of the mark width on all sides. Minimum practical sizes are 16px for the favicon mark, 20px for navigation chrome, 32px for prominent UI placement, and 180px wide for the wordmark.
+Clear space should be at least one quarter of the visible monogram width on all sides. Minimum practical rendered widths are about 48px for mobile chrome, 54px for sidebar chrome, 72px for workspace headers, and 180px for documentation or marketing placement.
-Do not rotate, stretch, recolor outside the approved palette, add glow effects, place on low-contrast imagery, replace the compression bands, or use the spectral gradient as ordinary text fill.
+Do not rotate, stretch, recolor, recreate with text, add containers by default, remove or exaggerate the cyan/ember edges, place on busy media, or use the logo's chromatic treatment as ordinary interface decoration. The logo's cyan and ember edges do not redefine the product palette; the interface remains slate, iris, and ember.
## Palette