Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions DESIGN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# TrustDesk — visual language

Portfolio surface for Fischer Product Lab. This is a cinematic, read-only demonstration — not a night SaaS, not a template.

## Tokens

| Token | Hex | Role |
| --- | --- | --- |
| Navy | `#0B1220` | Void. Page, canvas, chrome. |
| Ivory | `#F4EFE4` | Type and the primary CTA fill. |
| Gold | `#C4A35A` | The only accent. Rules, meta, rim-light, secondary CTA. |

Atmosphere is one thing: **film grain + a single gold/ivory light** sitting over the product window. Status colors (approved / review / danger) exist only inside the product chrome, where they mean something.

## Type

Face: **Geist**. Mono: **Geist Mono**. Loaded via `next/font`. The live demo screens keep Hanken Grotesk + Fraunces so the product chrome stays itself.

Four sizes, no more:

| Name | Use |
| --- | --- |
| Display | Hero headline only |
| Title | Section heads |
| Body | Subcopy and how-it-works |
| Meta | Eyebrows, nav, footer — mono, tracked, uppercase |

## Kill list

- No purple. No indigo mesh. No default shadcn violet.
- No Inter as the face.
- No emoji icons.
- No Spline / R3F orb.
- No fake “trusted by” logos.
- No five CTAs. Two: **Open the demo** and **See how it decides**.
- No auth, Stripe, CMS, or a second product.
- Do not copy BridgeMind copy, logo, or blue beam. Steal the register: void, one light, grain, grotesque headline, real product under the light.
22 changes: 13 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ Five screens:

| Route | Screen | Purpose |
| --- | --- | --- |
| `/` | **Executive Dashboard** | Open questionnaires, automation rate, hours saved, revenue supported, legal/privacy escalations, SLA risk, and top control categories. |
| `/` | **Marketing** | Cinematic portfolio landing — hero, product chrome, how the engine decides. Visual language in [`DESIGN.md`](./DESIGN.md). |
| `/demo` | **Executive Dashboard** | Open questionnaires, automation rate, hours saved, revenue supported, legal/privacy escalations, SLA risk, and top control categories. |
| `/questionnaires` | **Questionnaire Workspace** | Every in-flight question with confidence, status, mapped evidence, and reviewer — searchable and filterable. |
| `/questionnaires/[id]` | **Questionnaire Detail** | One customer's request in full: each drafted answer, its five confidence factors, mapped evidence with live freshness, routing, and reasons. |
| `/controls` | **Control & Evidence Library** | Approved response language with framework mappings, owners, and freshness — stale evidence surfaced first. |
Expand Down Expand Up @@ -86,17 +87,20 @@ pnpm test

```
trustdesk/
├── DESIGN.md # Locked tokens, type scale, kill list
├── app/
│ ├── page.tsx # Executive dashboard (/)
│ ├── questionnaires/
│ │ ├── page.tsx # Questionnaire workspace
│ │ └── [id]/page.tsx # Questionnaire detail
│ ├── controls/page.tsx # Control & evidence library
│ ├── brief/page.tsx # Executive brief
│ ├── about/page.tsx # About this demo
│ ├── layout.tsx # Root layout + app shell
│ ├── (marketing)/page.tsx # Cinematic landing (/)
│ ├── (demo)/
│ │ ├── layout.tsx # App shell for the live demo
│ │ ├── demo/page.tsx # Executive dashboard (/demo)
│ │ ├── questionnaires/ # Workspace + detail
│ │ ├── controls/page.tsx
│ │ ├── brief/page.tsx
│ │ └── about/page.tsx
│ ├── layout.tsx # Root fonts + metadata
│ └── globals.css # Design system (Tailwind v4 theme)
├── components/
│ ├── marketing/ # Landing hero + product window
│ ├── layout/ # Sidebar, top bar, app shell
│ ├── dashboard/ # Status donut, category bar
│ ├── question/ # Confidence ring, factor breakdown
Expand Down
2 changes: 1 addition & 1 deletion app/about/page.tsx → app/(demo)/about/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ export default function AboutPage() {
Built by Trevor Fischer · Fischer Product Lab
</p>
<Link
href="/"
href="/demo"
className="text-sm text-ink-muted transition-colors hover:text-gold"
>
Back to dashboard
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
13 changes: 13 additions & 0 deletions app/(demo)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { AppShell } from "@/components/layout/app-shell";

export default function DemoLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<div className="demo-shell min-h-dvh">
<AppShell>{children}</AppShell>
</div>
);
}
File renamed without changes.
5 changes: 5 additions & 0 deletions app/(marketing)/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { LandingPage } from "@/components/marketing/landing-page";

export default function MarketingPage() {
return <LandingPage />;
}
182 changes: 159 additions & 23 deletions app/globals.css
Original file line number Diff line number Diff line change
@@ -1,57 +1,64 @@
@import "tailwindcss";

/*
TrustDesk design system — "executive trust command center".
Defining colors/fonts here (Tailwind v4 @theme) generates utilities
like bg-canvas, text-ink, border-hairline, font-display, etc.
Shares the Fischer Product Lab palette: near-black navy, ivory, gold.
TrustDesk design system — Fischer Product Lab.
Tokens live here so Tailwind v4 emits utilities like bg-canvas, text-ink,
border-hairline, font-display, font-marketing.
See DESIGN.md for the locked language and the kill list.
*/
@theme {
/* Surfaces (near-black navy, layered) */
--color-canvas: #0b0f17;
/* Surfaces — void navy, layered */
--color-navy: #0b1220;
--color-canvas: #0b1220;
--color-surface: #0f1521;
--color-surface-2: #141c2b;
--color-hairline: #1e2738;
--color-hairline-strong: #2b3754;

/* Text (ivory) */
--color-ink: #f5efe1;
/* Text — ivory */
--color-ivory: #f4efe4;
--color-ink: #f4efe4;
--color-ink-muted: #a7aebc;
--color-ink-faint: #6c7689;

/* Accents */
--color-gold: #c9a45c;
/* One accent — gold */
--color-gold: #c4a35a;
--color-gold-soft: #e3c894;
--color-data: #9cc9ff;

/* Answer-status + evidence colors */
--color-approved: #5fd6a0; /* Auto-Approved / Current evidence */
--color-suggested: #9cc9ff; /* Suggested draft */
--color-review: #e6a560; /* Needs Review / Expiring Soon */
--color-danger: #e8736f; /* Escalate / Expired evidence */
/* Product semantics (status only — never atmosphere) */
--color-data: #9cc9ff;
--color-approved: #5fd6a0;
--color-suggested: #9cc9ff;
--color-review: #e6a560;
--color-danger: #e8736f;

/* Fonts (wired to next/font variables in layout.tsx) */
/* Type */
--font-sans: var(--font-hanken), ui-sans-serif, system-ui, sans-serif;
--font-display: var(--font-fraunces), Georgia, "Times New Roman", serif;
--font-marketing: var(--font-geist), ui-sans-serif, system-ui, sans-serif;
--font-mono: var(--font-geist-mono), ui-monospace, "IBM Plex Mono", monospace;
}

html {
color-scheme: dark;
scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
}

body {
background-color: var(--color-canvas);
background-image:
radial-gradient(60rem 60rem at 12% -10%, rgba(201, 164, 92, 0.06), transparent 60%),
radial-gradient(50rem 50rem at 100% 0%, rgba(156, 201, 255, 0.05), transparent 55%);
background-attachment: fixed;
background-color: var(--color-navy);
color: var(--color-ink);
font-family: var(--font-sans);
-webkit-font-smoothing: antialiased;
}

::selection {
background-color: rgba(201, 164, 92, 0.28);
background-color: rgba(196, 163, 90, 0.28);
color: var(--color-ink);
}

Expand All @@ -69,3 +76,132 @@ body {
border: 2px solid transparent;
background-clip: content-box;
}

/* Demo shell — faint gold wash only. No indigo mesh. */
.demo-shell {
background-image: radial-gradient(
50rem 36rem at 8% -8%,
rgba(196, 163, 90, 0.06),
transparent 58%
);
background-attachment: fixed;
}

/* -------------------------------------------------------------------------- */
/* Marketing landing — one light, grain, four type sizes */
/* -------------------------------------------------------------------------- */

.marketing {
--type-display: clamp(3.25rem, 9vw, 6.25rem);
--type-title: clamp(1.375rem, 2.4vw, 1.75rem);
--type-body: 1.0625rem;
--type-meta: 0.75rem;
position: relative;
isolation: isolate;
min-height: 100dvh;
font-family: var(--font-marketing);
color: var(--color-ivory);
background-color: var(--color-navy);
}

.marketing-type-display {
font-size: var(--type-display);
line-height: 0.92;
letter-spacing: -0.045em;
font-weight: 600;
}

.marketing-type-title {
font-size: var(--type-title);
line-height: 1.2;
letter-spacing: -0.03em;
font-weight: 500;
}

.marketing-type-body {
font-size: var(--type-body);
line-height: 1.55;
}

.marketing-type-meta {
font-family: var(--font-mono);
font-size: var(--type-meta);
line-height: 1.4;
letter-spacing: 0.16em;
text-transform: uppercase;
}

/* Single light event — gold/ivory pool sitting on the product window */
.marketing-light {
position: absolute;
top: -28%;
left: 50%;
z-index: 0;
width: min(56rem, 100vw);
height: 70%;
transform: translateX(-50%);
pointer-events: none;
background: radial-gradient(
ellipse 58% 62% at 50% 38%,
rgba(244, 239, 228, 0.28) 0%,
rgba(196, 163, 90, 0.2) 26%,
rgba(196, 163, 90, 0.07) 52%,
transparent 70%
);
}

.marketing-grain {
position: fixed;
inset: 0;
z-index: 40;
pointer-events: none;
opacity: 0.32;
mix-blend-mode: overlay;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
}

.product-window {
box-shadow:
0 0 0 1px rgba(196, 163, 90, 0.42),
0 0 48px -10px rgba(196, 163, 90, 0.28),
0 -28px 90px -24px rgba(244, 239, 228, 0.1),
0 48px 90px -28px rgba(0, 0, 0, 0.7);
}

@keyframes marketing-rise {
from {
opacity: 0;
transform: translateY(22px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

.marketing-rise {
animation: marketing-rise 0.9s cubic-bezier(0.22, 1, 0.36, 1) both;
}

.marketing-rise-delay-1 {
animation-delay: 0.08s;
}
.marketing-rise-delay-2 {
animation-delay: 0.16s;
}
.marketing-rise-delay-3 {
animation-delay: 0.28s;
}
.marketing-rise-delay-4 {
animation-delay: 0.42s;
}

@media (prefers-reduced-motion: reduce) {
.marketing-rise,
.marketing-rise-delay-1,
.marketing-rise-delay-2,
.marketing-rise-delay-3,
.marketing-rise-delay-4 {
animation: none;
}
}
21 changes: 15 additions & 6 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import type { Metadata } from "next";
import { Fraunces, Hanken_Grotesk } from "next/font/google";
import { Fraunces, Geist, Geist_Mono, Hanken_Grotesk } from "next/font/google";
import "./globals.css";
import { AppShell } from "@/components/layout/app-shell";

const geist = Geist({
subsets: ["latin"],
variable: "--font-geist",
display: "swap",
});

const geistMono = Geist_Mono({
subsets: ["latin"],
variable: "--font-geist-mono",
display: "swap",
});

const fraunces = Fraunces({
subsets: ["latin"],
Expand Down Expand Up @@ -29,11 +40,9 @@ export default function RootLayout({
return (
<html
lang="en"
className={`${fraunces.variable} ${hankenGrotesk.variable} h-full`}
className={`${geist.variable} ${geistMono.variable} ${fraunces.variable} ${hankenGrotesk.variable} h-full`}
>
<body className="min-h-dvh">
<AppShell>{children}</AppShell>
</body>
<body className="min-h-dvh">{children}</body>
</html>
);
}
2 changes: 1 addition & 1 deletion components/layout/nav-items.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export interface NavItem {
}

export const navItems: NavItem[] = [
{ href: "/", label: "Dashboard", icon: LayoutDashboard, exact: true },
{ href: "/demo", label: "Dashboard", icon: LayoutDashboard, exact: true },
{ href: "/questionnaires", label: "Questionnaires", icon: ClipboardList },
{ href: "/controls", label: "Control Library", icon: Library },
{ href: "/brief", label: "Executive Brief", icon: FileText },
Expand Down
Loading
Loading