diff --git a/THEME_AND_BRANDING.md b/THEME_AND_BRANDING.md
index f94ffaf..379c65d 100644
--- a/THEME_AND_BRANDING.md
+++ b/THEME_AND_BRANDING.md
@@ -376,6 +376,40 @@ colors: {
---
+## Brand Marks
+
+The brewery's primary brand mark is the **hoppy turtle** — a monochrome
+line-art anchor/turtle/hop mark. The stacked wordmark is secondary and is
+not used at icon sizes.
+
+### Mark inventory (`public/`)
+
+| File | Artwork | Used for |
+| --- | --- | --- |
+| `brand/hoppy-turtle-{black,white}-1600.png` | Full mark, 1600px master | Large placements only — never icons or email headers |
+| `brand/email-mark-{black,white}.png` | Same mark, 600×503 | Site UI (`components/brand-mark.tsx`), outbound trade email header (black variant) |
+| `brand/avatar-{light,dark}-512.png` | Circular badge on solid bg | App-icon source (apple-touch-icon, android-chrome) |
+| `brand/avatar-transparent-{black,white}-512.png` | Circular badge, transparent | Small admin/UI badges (trade timeline outbound avatar) |
+| `favicon.ico`, `favicon-{16x16,32x32}{,-dark}.png`, `favicon-48x48.png`, `favicon.svg` | Circular badge raster set | Browser favicons — the SVG adapts to dark browser chrome via `prefers-color-scheme` |
+
+### Rules
+
+* **Monochrome only.** Black artwork on light surfaces, white artwork on
+ dark surfaces. Never recolor, invert with CSS filters, or place color
+ variants.
+* **Tiny contexts use the circular badge** (`avatar-*`, `favicon-*`),
+ never the full mark or wordmark — the linework collapses below ~32px.
+* Site UI uses `components/brand-mark.tsx` (`tone="black"|"white"`,
+ `decorative` when adjacent text already names the brand).
+* Email uses absolute URLs on the canonical site origin — relative paths
+ never resolve inside a mail client.
+* App icons are `purpose: "any"`; no maskable assets exist yet (the badge
+ ring would clip in the safe zone) — a dedicated maskable asset is a
+ possible follow-up.
+* BIMI (the turtle in the inbox sender column) is a future
+ email-infrastructure task — it needs a VMC + DNS work and is not
+ implemented.
+
## Success Criteria
The theme is correct when:
* The site is calm and readable
diff --git a/app/layout.tsx b/app/layout.tsx
index 06d38b5..f4442d4 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -105,8 +105,35 @@ export const metadata: Metadata = {
},
icons: {
icon: [
- { url: "/favicon-16x16.png", sizes: "16x16", type: "image/png" },
- { url: "/favicon-32x32.png", sizes: "32x32", type: "image/png" },
+ // SVG first — the circular badge mark adapts to light/dark browser
+ // chrome internally via prefers-color-scheme. PNG fallbacks below
+ // carry explicit media hints for browsers that ignore the SVG.
+ { url: "/favicon.svg", type: "image/svg+xml" },
+ {
+ url: "/favicon-16x16.png",
+ sizes: "16x16",
+ type: "image/png",
+ media: "(prefers-color-scheme: light)",
+ },
+ {
+ url: "/favicon-16x16-dark.png",
+ sizes: "16x16",
+ type: "image/png",
+ media: "(prefers-color-scheme: dark)",
+ },
+ {
+ url: "/favicon-32x32.png",
+ sizes: "32x32",
+ type: "image/png",
+ media: "(prefers-color-scheme: light)",
+ },
+ {
+ url: "/favicon-32x32-dark.png",
+ sizes: "32x32",
+ type: "image/png",
+ media: "(prefers-color-scheme: dark)",
+ },
+ { url: "/favicon-48x48.png", sizes: "48x48", type: "image/png" },
{ url: "/favicon.ico" },
],
apple: "/apple-touch-icon.png",
diff --git a/components/admin-trade-workspace.tsx b/components/admin-trade-workspace.tsx
index 1b5cdad..2c41ab6 100644
--- a/components/admin-trade-workspace.tsx
+++ b/components/admin-trade-workspace.tsx
@@ -51,6 +51,7 @@ import {
type TradeLeadView,
} from "@/lib/trade-leads-admin-common";
import {
+ splitQuotedEmailText,
TRADE_EMAIL_BODY_MAX,
TRADE_EMAIL_SUBJECT_MAX,
type TradeLeadCommunicationView,
@@ -1340,12 +1341,28 @@ export function AdminTradeWorkspace({ user }: { user: AdminPanelUser }) {
const comm = commId
? communicationsById.get(commId)
: undefined;
+ const isOutboundEmail =
+ comm?.direction === "outbound";
return (
-
+ {isOutboundEmail ? (
+ // Outbound DDB mail gets the brand avatar so it
+ // reads as "from us" at a glance; inbound +
+ // system activity keep the generic icons.
+ // eslint-disable-next-line @next/next/no-img-element -- static 16px mark; Image adds no benefit here
+
+ ) : (
+
+ )}
{formatAdminDateTime(activity.createdAt)}
@@ -1366,45 +1383,15 @@ export function AdminTradeWorkspace({ user }: { user: AdminPanelUser }) {
{comm?.direction === "outbound" && (
)}
- {comm?.direction === "outbound" &&
- (comm.deliveryState === "queued" ||
- comm.deliveryState === "failed") &&
- comm.subject &&
- comm.textBody && (
-
- )}
- {comm?.direction === "inbound" &&
- detail.lead.email && (
-
- )}
{comm && (
-
+ {comm.subject && (
+
+ {comm.subject}
+
+ )}
+
{comm.direction === "inbound"
? `From ${comm.from ?? "unknown sender"}`
: `To ${comm.to.join(", ") || "unknown"}`}
@@ -1415,12 +1402,83 @@ export function AdminTradeWorkspace({ user }: { user: AdminPanelUser }) {
} (not stored)`
: ""}
- {comm.textBody && (
-
- {comm.textBody}
- {comm.truncated ? " …" : ""}
-
- )}
+ {comm.textBody &&
+ (() => {
+ // Inbound mail usually quotes our
+ // earlier message — the reply text stays
+ // primary while the quote collapses.
+ const body =
+ comm.direction === "inbound"
+ ? splitQuotedEmailText(comm.textBody)
+ : { fresh: comm.textBody, quoted: null };
+ return (
+ <>
+
+ {body.fresh}
+ {comm.truncated ? " …" : ""}
+
+ {body.quoted && (
+
+
+ Quoted history
+
+
+ {body.quoted}
+
+
+ )}
+ >
+ );
+ })()}
+ {(comm.direction === "outbound" &&
+ (comm.deliveryState === "queued" ||
+ comm.deliveryState === "failed") &&
+ comm.subject &&
+ comm.textBody) ||
+ (comm.direction === "inbound" &&
+ detail.lead.email) ? (
+
+ {comm.direction === "outbound" &&
+ (comm.deliveryState === "queued" ||
+ comm.deliveryState === "failed") &&
+ comm.subject &&
+ comm.textBody && (
+
+ )}
+ {comm.direction === "inbound" &&
+ detail.lead.email && (
+
+ )}
+
+ ) : null}
)}
{!isNote && activity.body && (
diff --git a/components/brand-mark.tsx b/components/brand-mark.tsx
new file mode 100644
index 0000000..00da8b6
--- /dev/null
+++ b/components/brand-mark.tsx
@@ -0,0 +1,46 @@
+import Image from "next/image";
+
+// Deep Dive Brewing hoppy turtle — the primary brand mark. Monochrome
+// only: the black file for light surfaces, the white file for dark
+// surfaces. Never recolor; the two supplied assets are the whole palette.
+// See THEME_AND_BRANDING.md.
+const MARKS = {
+ black: "/brand/email-mark-black.png",
+ white: "/brand/email-mark-white.png",
+} as const;
+
+// Natural size of the supplied mark files — next/image keeps the 600:503
+// aspect so any rendered width stays undistorted.
+const MARK_WIDTH = 600;
+const MARK_HEIGHT = 503;
+
+export interface BrandMarkProps {
+ // Which artwork file: "black" on light backgrounds, "white" on dark.
+ tone: "black" | "white";
+ // Rendered width in CSS pixels; height follows the mark's aspect ratio.
+ size?: number;
+ // True when adjacent text already names the brand — renders alt="" so
+ // the mark is skipped by assistive tech instead of doubling the name.
+ decorative?: boolean;
+ className?: string;
+}
+
+export function BrandMark({
+ tone,
+ size = 32,
+ decorative = false,
+ className,
+}: BrandMarkProps) {
+ return (
+
+ );
+}
diff --git a/components/site-footer.tsx b/components/site-footer.tsx
index 6790b86..43ebc4f 100644
--- a/components/site-footer.tsx
+++ b/components/site-footer.tsx
@@ -1,4 +1,5 @@
import Link from "next/link";
+import { BrandMark } from "@/components/brand-mark";
import { ConsentSettingsLink } from "@/components/consent-settings-link";
import { SOCIAL_URLS } from "@/lib/site";
@@ -8,7 +9,10 @@ export function SiteFooter() {
-
Deep Dive Brewing Co
+
+
+ Deep Dive Brewing Co
+
Craft beer, brewed on Saba.
diff --git a/components/site-header-default.tsx b/components/site-header-default.tsx
index 84aa657..afd9ce4 100644
--- a/components/site-header-default.tsx
+++ b/components/site-header-default.tsx
@@ -1,4 +1,5 @@
import Link from "next/link";
+import { BrandMark } from "@/components/brand-mark";
import { MobileMenu } from "@/components/mobile-menu";
const navLinks = [
@@ -15,8 +16,9 @@ export function SiteHeaderDefault() {
+
Deep Dive Brewing Co