// phone-mock.jsx — iPhone frame + faithful screen recreations, synced to the
// CURRENT project_blackdevil source (July 2026):
// Today/Desk/Ledger + nav → "Shift Board" (design_handoff_shift_board_v2):
// 0-radius, corner-cut CTAs, flush near-black/paper surfaces, mono
// uppercase instrument labels, safety-orange (#FF5C00 dark / #C4502A
// light terracotta) — from src/components/v2/TodaySurface.tsx,
// CommandBarV2.tsx, src/components/desk/{DeskView,DecisionCard}.tsx,
// src/components/v2/LedgerView.tsx, src/app/globals.css (--sb-* tokens).
// Composer / Job record / Customers → still the warm "Rams" rounded-card
// system (not yet Shift-Board-converted) but repainted to the SAME new
// brand hex, since --brand-rams was repointed as part of the July 2 brand
// unification — from src/components/v2/ComposerView.tsx,
// src/components/v2/JobWorksurface.tsx, src/components/v2/CustomerWorksurface.tsx.
const { useEffect, useRef } = React;
// EKG waveform path — same as the actual product's logo (CommandBarV2's
// WerkdPulse / HomeUI HEART_PATH).
const EKG_PATH = 'M2 20L10 20L17 36L28 4L36 36L47 4L54 20L62 20';
// Inline lucide-style icon component (no external library needed at runtime —
// these stroke paths come from lucide.dev).
function Icon({ name, size = 18, color = 'currentColor', strokeWidth = 1.75, style }) {
const paths = {
'map-pin': <>>,
'navigation': <>>,
'phone': <>>,
'clock': <>>,
'alert-circle': <>>,
'bell': <>>,
'chevron-right': <>>,
'chevron-left': <>>,
'edit': <>>,
'trash': <>>,
'mail': <>>,
'share-2': <>>,
'star': <>>,
'more-vertical': <>>,
'send': <>>,
'plus': <>>,
'check-circle': <>>,
'dollar': <>>,
'file-text': <>>,
'calendar': <>>,
'trending-up': <>>,
'alert-triangle': <>>,
'home': <>>,
'briefcase': <>>,
'camera': <>>,
'mic': <>>,
'message-circle': <>>,
'wrench': <>>,
'x': <>>,
'user': <>>,
'signal': <>>,
'wifi': <>>,
'battery': <>>,
'battery-full': <>>,
'pause': <>>,
'play': <>>,
'chevron-down': <>>,
'dollar-sign': <>>,
'message-square': <>>,
'sparkles': <>>,
'search': <>>,
'bookmark': <>>,
'receipt': <>>,
'settings': <>>,
'zap': <>>,
'package': <>>,
'clipboard-list': <>>,
'credit-card': <>>,
'user-plus': <>>,
'bar-chart': <>>,
'tag': <>>,
'check': <>>,
'users': <>>,
'moon': <>>,
'book-open': <>>,
'route': <>>,
'shield-check': <>>,
'calendar-clock': <>>,
'inbox': <>>,
};
return (
);
}
// Werkd EKG logo mark (waveform, no badge/background or glow — bare mark only).
function WerkdMark({ size = 40, color = '#FF5C00' }) {
// No container box: the mark is just the waveform glyph. Sized ~20% larger
// (relative to the old badge-relative sizing) since there's no padding to
// eat into anymore. Aspect ratio matches the viewBox (64:40 = 1.6:1).
const h = size * 0.5;
const w = h * 1.6;
return (
);
}
// Werkd wordmark — official spec.
// Inter 800, letter-spacing -0.05em.
// "werk" in main color, "d" + "." in brand orange.
function WerkdWordmark({ size = 32, color = '#171717', dotColor = '#FF5C00' }) {
return (
werkd.
);
}
// Phone frame — iPhone-ish with screen content as children. Glove-friendly: matches the product's
// safe-area + bottom-nav presence.
function PhoneFrame({ children, width = 340, scale = 1, theme = 'dark', tilt = 0, glow = false }) {
const h = width * 2.16; // ~ iPhone 15 Pro aspect
return (
{/* Outer titanium bezel */}
{/* Inner black bezel */}
{/* Screen */}
{/* Dynamic Island */}
{children}
);
}
// ── Shift Board (design_handoff_shift_board_v2) token set ──────────────────
// Today / Desk / Ledger + the nav are fully converted to this system:
// 0-radius everywhere (circles + the 8px nav badge are the only exceptions),
// corner-cut CTAs (clip-path notch), flush near-black (dark) / warm paper
// (light) surfaces, mono uppercase tracked instrument labels. Values lifted
// verbatim from src/app/globals.css --sb-* custom properties.
function SBC(isDark) {
return isDark ? {
bg: '#0c0d0f', bgNav: '#101114', panel: '#14161a',
hairline: '#1b1e22', border: '#26292e', borderStrong: '#33373d',
text: '#f2f2f0', textHigh: '#e7e8e9', text2: '#c9cbce', muted: '#9a9ca0',
faint: '#6e7176', ghost: '#43474d',
brand: '#ff5c00', brandText: '#ff9257',
danger: '#ff4747', dangerText: '#ff6b6b', warn: '#e8a344', money: '#4ade80',
hazardBg: 'repeating-linear-gradient(-45deg, #2a1206 0 6px, #1a0c04 6px 12px)',
onBrand: '#0c0d0f',
} : {
bg: '#f6f2ea', bgNav: '#f1ece1', panel: '#fdfcf8',
hairline: '#e4ddce', border: '#dad3c4', borderStrong: '#c9c1b0',
text: '#201d18', textHigh: '#2a2620', text2: '#4a453c', muted: '#6b655b',
faint: '#8a8478', ghost: '#b5ac99',
brand: '#c4502a', brandText: '#b04724',
danger: '#d03b2f', dangerText: '#b0342a', warn: '#9a6b1f', money: '#2e6b44',
hazardBg: 'repeating-linear-gradient(-45deg, #f5ded6 0 6px, #efd2c8 6px 12px)',
onBrand: '#f6f2ea',
};
}
const SB_CUT_HERO = 'polygon(0 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%)';
const SB_CUT_CARD = 'polygon(0 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%)';
// SB mono section label — 0.22em tracked uppercase, orange when accent.
function SBLabel({ C, children, right, accent }) {
return (
{children}
{right && {right}}
);
}
// 38px→31px SB header circle (Map / Calendar / Settings).
function SBCircle({ C, icon, size = 31 }) {
return (
);
}
// SB header — wordmark ("d." orange) + mono meta line, 3 circles right.
function SBHeader({ C, meta, icons }) {
return (
Werkd.
{meta && (
{meta}
)}
{icons.map((ic, i) => )}
);
}
// SB stat strip — 3 cells, 1px rules, mono values over tracked mono labels.
function SBStatRow({ C, cols }) {
return (
{cols.map((c, i) => (
0 ? '1px solid ' + C.border : 'none' }}>
{c.value}
{c.label}
))}
);
}
// SB hairline notice row — 3px square rail, body title + faint sub, money/action right.
function SBNoticeRow({ C, tone, label, sub, right, action, first }) {
const rail = tone === 'danger' ? C.danger : tone === 'warning' ? C.warn : C.brand;
return (
{label}
{sub && {sub}}
{right && {right}}
{action ? (
{action}
) : (
›
)}
);
}
// v2 bottom nav — Today · Desk · Ledger + far-right ⊕ FAB (CommandBarV2.tsx).
// Flush bg-nav bar, 1px top border. Active = safety orange, mono 9px tracked
// caps, NO underline/notch (spec explicitly drops it). FAB is a dominant
// orange circle carrying the Werkd pulse mark, ringed by the bar's own bg so
// it "cuts" out of the top border.
function CommandBar({ theme = 'dark', active = 'today', deskCount = 2 }) {
const C = SBC(theme === 'dark');
const tabs = [
{ id: 'today', label: 'Today', icon: 'home' },
{ id: 'desk', label: 'Desk', icon: 'inbox', badge: deskCount },
{ id: 'ledger', label: 'Ledger', icon: 'search' },
];
return (
{tabs.map((tab) => {
const on = active === tab.id;
return (
{tab.badge > 0 && (
{tab.badge}
)}
{tab.label}
);
})}
{/* ⊕ Composer FAB — far-right, dominant, rides above the bar */}
);
}
function V2HomeIndicator({ isDark }) {
return (
);
}
// TODAY — "The Line" (TodaySurface.tsx, Shift Board). Header → NEXT JOB label
// + big instrument clock + leave/late chip → hero panel (name/address/desc +
// Navigate & start + Call/AI job prep) → stat row → Needs review → Queue.
function TodayScreen({ theme = 'dark', state = 'default', spotlight = false }) {
const isDark = theme === 'dark';
const C = SBC(isDark);
return (
Next job
{/* Instrument clock — big bold hour, stacked mins + AM/PM, mono chip */}
THU, JUL 2
{/* Hero instrument panel — fill + border, 4px tone rail */}
Aria Russell
30 min drive
2847 Birch Lane
Remove old thermostat, install smart, verify zone wiring.
Navigate & start
Needs review
Queue
{[
{ t: '11:00A', n: 'Dave Wilson', d: 'Outlet repair', a: '$140', done: false },
{ t: '8:00A', n: 'Mike — Drain clear', d: null, a: '$185', done: true },
].map((j, i) => (
{j.t}
{j.n}
{j.d ? {j.d}
: DONE · {j.a}}
{!j.done && {j.a}}
))}
);
}
function V2StatusBar({ C }) {
return (
);
}
// ── Rams (not yet Shift-Board-converted) token set — Composer, Job record,
// Customers still use these rounded warm surfaces. Brand hex updated to the
// July 2 unification (--brand-rams now resolves to the same new orange).
function V2C(isDark) {
return isDark ? {
bg: '#181714', card: '#22201c', control: '#302c26',
border: '#38322b', strong: '#453e34',
text: '#f5f5f7', text2: '#a8a8b0', text3: '#8a8a92', muted: '#6c6c74',
brand: '#ff5c00', ink: '#0c0d0f',
danger: '#ee5555', warn: '#e8a344', success: '#3cc26d',
brandMuted: 'rgba(255,92,0,0.12)', dangerMuted: 'rgba(238,85,85,0.12)',
dim: 'rgba(0,0,0,0.5)',
} : {
bg: '#f8f4ed', card: '#fffdfa', control: '#f0ece6',
border: '#e3d8c9', strong: '#d3c6b3',
text: '#171717', text2: '#6f7177', text3: '#9a9ca3', muted: '#9a9ca3',
brand: '#c4502a', ink: '#f6f2ea',
danger: '#dc2626', warn: '#d97706', success: '#16a34a',
brandMuted: 'rgba(196,80,42,0.10)', dangerMuted: 'rgba(220,38,38,0.10)',
dim: 'rgba(0,0,0,0.32)',
};
}
function V2Label({ C, children, right, dot, dotColor }) {
return (
{dot && }
{children}
{right && {right}}
);
}
// DESK — "Decisions, not documents" (DeskView.tsx + DecisionCard.tsx, Shift
// Board). 76px instrument count hero, mono kind labels + 4px tone rail cards,
// corner-cut CTAs, "Werkd did" journal.
function DeskScreen({ theme = 'dark' }) {
const isDark = theme === 'dark';
const C = SBC(isDark);
const cards = [
{ rail: C.danger, kind: 'COLLECT · $1,847', kc: C.dangerText, title: 'Mike Chen — invoice 14 days late', receipt: 'Friendly-but-firm reminder drafted · 3rd nudge', amt: '$1,847', cta: 'Send reminder', adjust: true },
{ rail: C.warn, kind: 'REVENUE', kc: C.warn, title: 'Aria Russell — job done, uninvoiced', receipt: 'Invoice drafted from today’s job', amt: '$420', cta: 'Review & send →', adjust: true },
{ rail: C.borderStrong, kind: 'CLOSE', kc: C.muted, title: 'Close out today', receipt: '1 job left to invoice · books tidy', amt: null, cta: 'Close the day', adjust: false },
];
return (
On your desk
3
Decisions
{cards.map((card, i) => (
{card.kind}
{card.title}
{card.amt && {card.amt}}
{card.receipt}
{card.cta}
{card.adjust && Adjust}
skip
))}
Werkd did
{[
{ t: '4:51', l: 'Invoice sent to Mike Chen — $185' },
{ t: '3:12', l: 'Quote follow-up sent to Dave Wilson' },
].map((j, i) => (
0 ? '1px solid ' + C.hairline : 'none' }}>
{j.t}
{j.l}
))}
);
}
// LEDGER — "Look it up, then leave" (LedgerView.tsx, Shift Board). Collected
// hero (money-green instrument numeral), pulse strip, AI brief ladder,
// omnibox, tools row, recents.
function LedgerScreen({ theme = 'dark' }) {
const isDark = theme === 'dark';
const C = SBC(isDark);
return (
Collected · June
$12,400
+18% vs May
3 invoices are overdue, $1,847 at risk.
Chase
{[['tag', 'Price book'], ['bar-chart', 'Reports'], ['users', 'Customers']].map(([ic, l], i) => (
{l}
))}
Recent
{[
{ ic: 'receipt', l: 'Invoice #1041 — Tom Parker', s: 'Paid · $1,240', sc: SBC(isDark).money },
{ ic: 'file-text', l: 'Quote #318 — Dave Wilson', s: 'Sent · expires in 6 days', sc: C.faint },
{ ic: 'user', l: 'Aria Russell', s: '3 jobs · $1,260 lifetime', sc: C.faint },
].map((r, i) => (
0 ? '1px solid ' + C.hairline : 'none' }}>
{r.l}
{r.s}
›
))}
);
}
const MoneyScreen = LedgerScreen; // v1 Money tab folded into Ledger
// ⊕ COMPOSER — capture-first create (ComposerView.tsx, still Rams-style):
// voice bar → "or pick one" create rows → sharp CTA.
function ComposerScreen({ theme = 'dark' }) {
const isDark = theme === 'dark';
const C = V2C(isDark);
const rows = [
{ icon: 'briefcase', t: 'New job', s: 'Book a customer and time', sel: true },
{ icon: 'file-text', t: 'New quote', s: 'Price up work to send' },
{ icon: 'receipt', t: 'New invoice', s: 'Bill a completed job' },
{ icon: 'credit-card', t: 'Log expense', s: 'Snap a receipt or cost' },
{ icon: 'camera', t: 'Photo diagnosis', s: 'Snap a photo, get a fix' },
];
return (
{/* dimmed Today behind the sheet */}
{/* the sheet */}
Just say what happened
✕
{/* voice bar */}
Hold to talk
Hold to talk · tap to type
{/* OR PICK ONE */}
Or pick one
{rows.map((r, i) => (
{r.t}
{r.s}
{r.sel
?
: }
))}
{/* sharp CTA (radius-cta 4) */}
Start New job →
);
}
const AIHubScreen = ComposerScreen; // retired v1 hub — kept as an alias
// JOB WORKSURFACE — the record-surface recipe (JobWorksurface.tsx, still
// Rams-style): ⌄ top bar, one card per concern, sticky one-CTA footer.
function JobRecordScreen({ theme = 'dark' }) {
const isDark = theme === 'dark';
const C = V2C(isDark);
const card = { border: '1px solid ' + C.strong, background: C.card, borderRadius: 12, padding: '9px 11px', marginTop: 9, boxShadow: isDark ? '0 1px 3px rgba(0,0,0,0.2)' : '0 1px 3px rgba(29,24,18,0.06)' };
const label = { fontSize: 8, fontWeight: 700, letterSpacing: '0.12em', textTransform: 'uppercase', color: C.text3, marginBottom: 2 };
const chip = (ic) => (
);
const link = (t) => {t} →;
const row = { display: 'flex', alignItems: 'center', gap: 9, padding: '7px 0' };
return (
{/* top bar: ⌄ minimize + status + kind pill */}
Aria Russell
2847 Birch Lane · Smart thermostat swap
Schedule
{chip('calendar')}
Sat, May 23 · 10:30 AM
Est. 1.5 hours
{link('Reschedule')}
Customer
{chip('phone')}
(555) 014-2288
CallText
{chip('map-pin')}
2847 Birch Lane
{link('Route')}
Job
{chip('clock')}
Remove old thermostat, install smart, verify zone wiring.
$420
Materials
{chip('package')}
Smart thermostat
$185
Photos
{chip('camera')}
2 photos added
{link('Manage')}
{/* sticky footer — ONE primary CTA + Adjust; the rest are text links */}
Start job →
Adjust
Reschedule
Cancel job
);
}
// Customer record — CustomerWorksurface.tsx (still Rams-style): identity +
// contact + history cards, "Full profile →" CTA. (The legacy fuller detail
// panel this used to recreate has been superseded by this leaner worksurface.)
function CustomersScreen({ theme = 'dark' }) {
const isDark = theme === 'dark';
const C = isDark ? {
bg: '#181714', surface: '#22201c', surface2: '#302c26',
text: '#f5f5f7', text2: '#a8a8b0', text3: '#8a8a92',
hairline: '#38322b', hairlineStrong: '#453e34',
brand: '#ff5c00', onBrand: '#0c0d0f', success: '#3cc26d', warn: '#e8a344',
successMuted: 'rgba(60,194,109,0.14)',
brandMuted: 'rgba(255,92,0,0.14)',
} : {
bg: '#f8f4ed', surface: '#fffdfa', surface2: '#f0ece6',
text: '#171717', text2: '#6f7177', text3: '#9a9ca3',
hairline: '#e3d8c9', hairlineStrong: '#d3c6b3',
brand: '#c4502a', onBrand: '#f6f2ea', success: '#16a34a', warn: '#d97706',
successMuted: 'rgba(22,163,74,0.10)',
brandMuted: 'rgba(196,80,42,0.10)',
};
return (
Customer
Aria Russell
2847 Birch Ln, Lakewood
{/* Contact card */}
Contact
{[
{ icon: 'phone', value: '(312) 555-0142', action: 'Call' },
{ icon: 'mail', value: 'aria.russell@email.com', action: 'Email' },
{ icon: 'map-pin', value: '2847 Birch Ln, Lakewood', action: 'Route', accent: true },
].map((r, i, arr) => (
0 ? '1px solid ' + C.hairline : 'none',
}}>
{r.value}
{r.action} →
))}
{/* History card */}
History
Last job Mar 8
$4,820 lifetime
{/* Notes card */}
Notes
Gate code 4417 · dog in yard. Prefers text over calls.
{/* Full profile CTA — footer, mirrors RecordWorksurface's single-CTA recipe */}
);
}
Object.assign(window, {
Icon, WerkdMark, WerkdWordmark, PhoneFrame, TodayScreen, CommandBar,
ComposerScreen, AIHubScreen, DeskScreen, LedgerScreen, MoneyScreen,
JobRecordScreen, CustomersScreen, EKG_PATH,
});