// direction-a.jsx — "Apple product page" treatment // Confident editorial typography, generous whitespace, chapter-style section titles // with the period. Light-first with dark hero + dark Assist section to break rhythm. const DA = { bg: '#fafaf7', bgAlt: '#f3f1ec', ink: '#0a0a0b', ink2: '#1a1a1c', text: '#1a1a1c', text2: '#5a5a60', text3: '#6e6e74', hairline: '#e0dcd0', hairlineSoft: '#b8b1a0', // Two AA-compliant tones, deliberately distinct (see docs/design/WERKD_VISUAL_LANGUAGE.md // for the product's own light/dark brand oranges, e67e22/ee9147 — same idea, this // marketing site's own tokens): // - brand: for TEXT and button-fills on LIGHT backgrounds. Matches the actual // product's current CTA/nav-accent color (#C4502A, sampled directly from the // app). White-on-fill measures ~4.64:1 (passes AA). As direct text/icon color // on this site's cream background it's ~4.44:1 — just under the 4.5 text // threshold, but dramatically better than the original #F97316's ~2.7:1. // - brandLight: for TEXT/icons on DARK backgrounds (hero, Why Werkd, Field Mode, // demo modal, and any dark card/column nested in an otherwise-light section). // Unchanged — the original vivid orange already passes ~6.9:1 against near-black. // Button/badge FILLS with white text are safe with `brand` regardless of the // surrounding page background (contrast there is fill-vs-its-own-text, not fill-vs-page). brand: '#C4502A', brandLight: '#F97316', font: 'var(--font-display)', }; window.DA = DA; // Real production destinations (app.werkd.pro). Auth is Clerk; legal routes // live on the app domain. External links open in a new tab. const W_BASE = 'https://app.werkd.pro'; const WERKD_URLS = { signIn: `${W_BASE}/sign-in`, signUp: `${W_BASE}/sign-up`, privacy: `${W_BASE}/privacy`, terms: `${W_BASE}/terms`, dpa: `${W_BASE}/dpa`, security: `${W_BASE}/security`, doNotSell: `${W_BASE}/do-not-sell`, contact: 'mailto:contact@werkd.pro', dmca: 'mailto:dmca@122ai.io', }; window.WERKD_URLS = WERKD_URLS; // Pricing — single source of truth lives in pricing-constants.js (loaded // before this file). Falls back to today's values if that script is missing. const P = window.WERKD_PRICING || { owner: 99, seat: 59, ownerAnnual: 999, seatAnnual: 599, trialDays: 14, annual: 1188 }; // ─── Responsive width model ────────────────────────────────────────────── // The page was authored at a fixed 1280px logical width. The standalone home // page sets window.__WERKD_RESPONSIVE = true and scales/relayouts by viewport. // Inside the comparison canvas the flag is unset, so components always render // the original 1280px desktop composition (the artboard handles its own size). // vw >= 1280 → logical = vw (fluid desktop, scale 1) // 760..1279 → logical = 1280 (desktop comp, scaled down to fit width) // < 760 → logical = 430 (true mobile layout, scaled to device width) function werkdLayout(vw) { if (vw >= 1280) return { logical: vw, scale: 1 }; if (vw >= 760) return { logical: 1280, scale: vw / 1280 }; return { logical: 430, scale: vw / 430 }; } function useWerkdWidth() { const resp = () => !!window.__WERKD_RESPONSIVE; const get = () => resp() ? werkdLayout(document.documentElement.clientWidth).logical : 1280; const [w, setW] = React.useState(get); React.useEffect(() => { if (!resp()) return; const on = () => setW(get()); window.addEventListener('resize', on); on(); return () => window.removeEventListener('resize', on); }, []); return w; } // isMobile when logical collapses to the 430 mobile track. function useIsMobile() { return useWerkdWidth() <= 430; } window.werkdLayout = werkdLayout; window.useWerkdWidth = useWerkdWidth; window.useIsMobile = useIsMobile; // Orange period only (wordmark style: "Werkd.") // dark: true when the ADot sits on a dark background (hero, Why Werkd, Field // Mode, demo modal, or a dark card/column nested in a light section) — uses // DA.brandLight instead of DA.brand since a plain doesn't inherit its // parent's color and needs to independently pick the AA-safe tone for its context. function ADot({ dark }) { return .; } // Orange last letter + orange period — used to brand-tail section headings. // e.g. "Today." renders as "Toda" black + "y." orange. function ATail({ letter, dark }) { return {letter}.; } // Reveal on scroll — fades + lifts in when in viewport. Plays once. function Reveal({ children, delay = 0, y = 28, as: As = 'div' }) { const ref = React.useRef(null); const reduced = window.usePrefersReducedMotion ? window.usePrefersReducedMotion() : false; const [shown, setShown] = React.useState(false); React.useEffect(() => { if (reduced) { setShown(true); return; } if (!ref.current) return; const obs = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) { setShown(true); obs.disconnect(); } }, { threshold: 0.12, rootMargin: '0px 0px -8% 0px' }); obs.observe(ref.current); return () => obs.disconnect(); }, [reduced]); return ( {children} ); } window.ATail = ATail; window.Reveal = Reveal; // Number counter — counts up from 0 to `to` once visible. Ease-out so it // decelerates onto the final value; raise `ease` for a longer, slower settle. function Counter({ to, suffix = '', prefix = '', duration = 2200, startDelay = 0, ease = 3, easeFn = null, threshold = 0.4, reserve = true, format = (n) => n.toLocaleString() }) { const [val, setVal] = React.useState(0); const ref = React.useRef(null); const startedRef = React.useRef(false); const reduced = window.usePrefersReducedMotion ? window.usePrefersReducedMotion() : false; React.useEffect(() => { if (reduced) { setVal(to); return; } // reduced motion: show final value, no count-up if (!ref.current) return; const obs = new IntersectionObserver(([e]) => { if (e.isIntersecting && !startedRef.current) { startedRef.current = true; const begin = () => { const start = performance.now(); const tick = (now) => { const t = Math.min(1, (now - start) / duration); const eased = easeFn ? easeFn(t) : 1 - Math.pow(1 - t, ease); setVal(eased * to); if (t < 1) requestAnimationFrame(tick); }; requestAnimationFrame(tick); }; if (startDelay > 0) setTimeout(begin, startDelay); else begin(); obs.disconnect(); } }, { threshold }); obs.observe(ref.current); return () => obs.disconnect(); }, [to, duration, startDelay, ease, easeFn, threshold, reduced]); // Reserve the final value's width so the layout never reflows as digits and // separators appear while counting (which otherwise nudges the whole card). if (reserve) { return ( {prefix}{format(Math.round(val))}{suffix} ); } return {prefix}{format(Math.round(val))}{suffix}; } // Slide-in from a direction. Useful for sequenced cards/rows. function Slide({ children, delay = 0, from = 'left', distance = 32 }) { const ref = React.useRef(null); const [shown, setShown] = React.useState(false); React.useEffect(() => { if (!ref.current) return; const obs = new IntersectionObserver(([e]) => { if (e.isIntersecting) { setShown(true); obs.disconnect(); } }, { threshold: 0.18 }); obs.observe(ref.current); return () => obs.disconnect(); }, []); const offX = from === 'left' ? -distance : from === 'right' ? distance : 0; const offY = from === 'up' ? distance : from === 'down' ? -distance : 0; return (
{children}
); } window.Counter = Counter; window.Slide = Slide; function APrimaryButton({ children, dark = false, big = false, onClick, href }) { const Tag = href ? 'a' : 'button'; const linkProps = href ? { href, target: '_blank', rel: 'noopener noreferrer' } : {}; return ( {children} ); } function ASecondaryButton({ children, dark = false, big = false, onClick, href }) { const Tag = href ? 'a' : 'button'; const linkProps = href ? { href, target: '_blank', rel: 'noopener noreferrer' } : {}; return ( {children} ); } // Top nav const NAV_LINKS = [ ['Why werkd', '#intelligence'], ['Today', '#today'], ['Ledger', '#ledger'], ['For Customers', '#customers'], ['Pricing', '#pricing'], ]; function ANav() { const m = useIsMobile(); const [open, setOpen] = React.useState(false); // Close the mobile menu if we leave the mobile track React.useEffect(() => { if (!m) setOpen(false); }, [m]); return ( ); } // HERO const SCRIM_DEFAULTS = /*EDITMODE-BEGIN*/{ "scrimTL": 0.82, "scrimTR": 0.48, "scrimBL": 0.95, "scrimBR": 0.55, "shadowOpacity": 0.45, "shadowBlur": 18, "eyebrowOpacity": 1, "eyebrowBlur": 7, "headlineGrayOpacity": 1, "showPhone": false, "textFullWidth": false }/*EDITMODE-END*/; function buildScrim(tl, tr, bl, br) { const c = 'rgba(10,10,11,'; return [ `radial-gradient(ellipse 75% 75% at 0% 0%, ${c}${tl}) 0%, transparent 65%)`, `radial-gradient(ellipse 75% 75% at 100% 0%, ${c}${tr}) 0%, transparent 65%)`, `radial-gradient(ellipse 75% 75% at 0% 100%, ${c}${bl}) 0%, transparent 65%)`, // Bottom-right is the most prominent corner: a larger, denser, softer pool // that both hides the footage watermark and bleeds well into the video. Its // solid-ish core (controlled by `br`) feathers all the way out — no hard edge. `radial-gradient(ellipse 135% 120% at 100% 100%, ${c}${br}) 0%, ${c}${Math.min(br, 0.9) * br}) 26%, ${c}${0.42 * br}) 52%, transparent 82%)`, `linear-gradient(90deg, ${c}0.55) 0%, ${c}0.10) 50%, ${c}0.18) 100%)`, ].join(', '); } function AHero() { const m = useIsMobile(); const TweaksPanel = window.TweaksPanel; const TweakSection = window.TweakSection; const TweakSlider = window.TweakSlider; const TweakToggle = window.TweakToggle; const [t, setTweak] = window.useTweaks(SCRIM_DEFAULTS); const scrim = buildScrim(t.scrimTL, t.scrimTR, t.scrimBL, t.scrimBR); // Static hero still (replaced the video loop, Jul 2026). One JPEG under the // same tweakable corner scrim. A one-time 5s settle-zoom (CSS, ends on its // own) adds life without needing WCAG 2.2.2's pause control (motion ≤ 5s), // and is disabled under prefers-reduced-motion. const showStill = !m; return (
{/* Dusk-driveway hero still, under the same tweakable corner scrim. Subject (tech + van) sits at the right where the phone overlaps it — the scrim keeps it a rim-lit silhouette so the phone stays dominant. */} {showStill && (
); } // TODAY chapter function AToday() { const m = useIsMobile(); return (
THE HOME SCREEN

Today

Every job. Every dollar. Every customer waiting. One screen.

{/* Phone + side callouts */}
{/* Left callouts — the Next Up card, the hero of Today */}
{/* Phone center — spotlighted on the Next Up card */}
{/* glow concentrated on the upper third, where the Next Up card sits */} {/* Right callouts — top-right money, the intelligence, the alerts (top→bottom to match the screen) */}
); } function Callout({ title, body, dir = 'left' }) { return (
{title}
{body}
); } // LEDGER chapter (light) — was "Money" under the old Today/Work/Office/Money // nav; that surface is now Ledger (Today/Desk/Ledger + ⊕), so the chapter // name follows the product's actual current IA instead of the retired one. function AMoney() { const m = useIsMobile(); return (
QUOTE → PAID

Ledger

Quote at the truck. Invoice from the front porch. Send the invoice before you leave the driveway.

Your customer gets a clean web page — no app to install, no account to make. Approve, pick a time, or pay with card or bank transfer through Stripe. Standard Stripe fees apply.
{/* Money capability strip */}
{[ ['Quotes', 'AI-drafted line items, you review and send.'], ['Invoices', 'Auto-drafted when you mark a job done. One tap to send.'], ['Reminders', 'Polite, escalating payment reminders — sent automatically on the schedule you set.'], ['Books', 'Expenses, mileage, profit & loss — organized monthly for your accountant.'], ['Snap the receipt', 'AI reads the vendor, amount, and every line item. Total logs as an expense, syncs to QuickBooks automatically when connected.'], ['Price Book learns', 'Every part on every receipt is saved at cost — what you paid. Future quotes pull your real prices, not guesses.'], ].map(([t, d], i) => (
= 2 : i >= 3) ? `1px solid ${DA.hairline}` : 'none', }}>
{t}
{d}
))}
{/* SnapExpense + Price Book callout */}
{/* Left: visual */}
HOME DEPOT #4821
{[ ['3/4" Ball valve', '$8.47'], ['SharkBite coupling (2)', '$14.90'], ['Teflon tape', '$2.15'], ['MISC misc items', '$1.08'], ].map(([name, price], i) => (
{name} {price}
))}
TOTAL $26.60
$26.60 expense logged
Synced to QuickBooks when connected · Materials
3 parts saved to Price Book
At cost · used in future quotes
MISC items filtered out automatically
{/* Right: copy */}
SNAPEXPENSE · PRICE BOOK

One receipt.
Two jobs done

Snap a receipt on the job — werkd reads every line item. The total goes straight to your books and syncs to QuickBooks when connected. Every real part goes into your Price Book at cost — what you actually paid, no markup baked in.

Next time you build a quote for a similar job, your part prices are already there. Your Price Book gets smarter with every receipt — no guessing, no looking it up.

{[ 'AI reads vendor, amount, and every line item', 'Junk lines (tax, fees, MISC) filtered out automatically', 'Parts saved at cost — markup applied at quote time', 'Total syncs to QuickBooks or Xero automatically — when connected', ].map((t, i) => (
{t}
))}
); } // Closer / footer CTA function ACloser() { const m = useIsMobile(); const footCols = [ { h: 'Product', links: [['Why werkd', '#intelligence'], ['Today', '#today'], ['Ledger', '#ledger'], ['For customers', '#customers'], ['Pricing', '#pricing'], ['ROI calculator', '#roi']] }, { h: 'Resources', links: [['The Real Cost of "AI Included"', 'real-cost-of-ai.html']] }, { h: 'Legal', links: [['Privacy', WERKD_URLS.privacy], ['Terms', WERKD_URLS.terms], ['DPA', WERKD_URLS.dpa], ['Security', WERKD_URLS.security], ['DMCA', WERKD_URLS.dmca], ['Do Not Sell My Info', WERKD_URLS.doNotSell]] }, ]; return (

What would you do with
your evenings back?

Free for 14 days. No card. No contract. Cancel anytime.

window.openEarlyAccess()}>Request early access → Questions? Email us
{/* Footer */}
{/* Top: brand + link columns */}
{/* Brand block */}
The AI business app for trades. You do the work — werkd does the rest.
Questions? contact@werkd.pro
{/* Link columns */} {footCols.map((col) => (
{col.h}
{col.links.map(([label, href]) => { const ext = /^https?:/.test(href); return ( {label} );})}
))}
{/* Bottom bar */}
© 2026 Werkd, Inc. All rights reserved.
Built for tradespeople.
); } // CUSTOMERS chapter function ACustomers() { const m = useIsMobile(); return (
YOUR PEOPLE

Customers

Every job. Every conversation. Every dollar — attached to the person who paid it. Werkd remembers, so you don't have to.

{/* CRM strip */}
{[ ['Helps catch duplicates', 'Imports collapse obvious duplicates on conflict and flag the rest for review.'], ['Smart suggestions', 'Quarterly checkups, renewal nudges, slow-pay flags — surfaced for your approval.'], ['Text from the card', 'One tap opens your phone to text them — their number and history right there.'], ['Outstanding balances', 'See exactly what they owe you, right on the card.'], ].map(([t, d], i) => (
= 2 ? `1px solid ${DA.hairline}` : 'none', }}>
{t}
{d}
))}
); } // FAQ function FAQItem({ q, a, isOpen, onToggle, m }) { const ref = React.useRef(null); const [h, setH] = React.useState(0); React.useLayoutEffect(() => { const measure = () => { if (ref.current) setH(ref.current.scrollHeight); }; measure(); window.addEventListener('resize', measure); return () => window.removeEventListener('resize', measure); }, [a, m]); return (
{a}
); } function AFAQ() { const items = [ { q: 'Do I need a separate scheduling app, CRM, invoicing tool, and bookkeeper?', a: 'No — Today, Desk, Ledger, plus the ⊕ button to capture anything. That\'s the whole app: scheduling, CRM, quotes, invoices, and payments in one. The books stay organized as you go, which shrinks the cleanup a bookkeeper would normally charge for. For taxes, payroll, and accounting advice, keep your accounting pro — werkd makes their job easier, it doesn\'t do it.', }, { q: 'Does it work offline? My basement signal is garbage.', a: 'Yes. Werkd keeps working offline — quotes, jobs, photos, and timers all keep going when you\'re in a crawl space or a dead zone. It syncs the moment you have signal again.', }, { q: 'Will my customers have to download an app?', a: 'They start with a link — no account, no App Store. After opening it, they can add it to their home screen in one tap for push notifications and quick access.', }, { q: 'How fast is "Photo Diagnosis," really?', a: 'Fast enough to draft a quote while you\'re still standing next to the unit. Werkd uses multiple frontier vision models to suggest a likely cause, propose materials and labor, and queue up a quote draft. You review the draft — nothing goes to the customer until you tap send. Use it as a second set of eyes, not a certified diagnosis.', }, { q: 'What about my QuickBooks, my Stripe, my existing customer list?', a: 'Stripe is the payments engine — it\'s built in. Spreadsheet / Google Contacts / QuickBooks all import in one screen, with duplicate-merge. When connected, every invoice and expense syncs to QuickBooks or Xero automatically as you go.', }, { q: 'I run a 4-truck crew. Can my techs use it without seeing the money side?', a: `Yes. Roles are baked in. Owners get the full app. Techs see Today — their jobs, their route, their daily log — at $${P.seat}/mo per tech. Dispatchers see the board. Nobody sees money unless you say so.`, }, { q: 'What trades does werkd work for?', a: 'Plumbing, electrical, HVAC, roofing, landscaping, appliance repair, flooring, concrete, pool service, pest control, septic, and general contracting — 17 trades modeled on day one, each with its own rate card and job catalog. If your trade isn\'t on the list, werkd still works — you\'d start with a blank rate card and build it your way.', }, { q: 'What happens to my data if I cancel?', a: 'It\'s yours. You can export your customers, invoices, quotes, and jobs from the app at any time — before or after you cancel. We don\'t hold your data hostage.', }, { q: 'What does it cost?', a: `$${P.owner}/mo for you, with everything included — portal, voice, photo diagnosis, books, dispatch, the works. Extra hands (techs and other non-admin users) are $${P.seat}/mo each. Free for ${P.trialDays} days, no card. No feature add-ons, no contract. Prefer annual? $${P.ownerAnnual}/year for the owner and $${P.seatAnnual}/year per extra tech, saving $${P.annual - P.ownerAnnual} and $${P.seat * 12 - P.seatAnnual}/seat. The calculator above (move the sliders) shows what most trades net.`, }, ]; const m = useIsMobile(); const [open, setOpen] = React.useState(null); return (

Questions

Short answers. Anything else? Email us at contact@werkd.pro

{items.map((it, i) => ( setOpen(open === i ? null : i)} /> ))}
); } function AHowItWorks() { const m = useIsMobile(); const steps = [ { n: '01', t: 'Book & route the day', d: 'Jobs land on your calendar and slot into your route automatically — no double-booking, no backtracking across town.', icon: 'calendar' }, { n: '02', t: 'Quote, work, invoice', d: 'Werkd drafts the quote on-site and the invoice when you mark the job done. You read it, tap approve, and send.', icon: 'file-text' }, { n: '03', t: 'Get paid & close out', d: 'Customers pay through your branded portal. Expenses, mileage, and books update themselves — ready for your accountant.', icon: 'dollar' }, ]; return (
HOW IT WORKS

The whole job, start to paid

Three steps, same as your day — werkd handles the booking, quoting, invoicing, and books in the background, so you can stay on the tools.

{steps.map((s, i) => (
{s.n}

{s.t}

{s.d}

))}
); } function AInlineCTA({ heading, sub }) { const m = useIsMobile(); return (
{heading}
{sub &&
{sub}
}
window.openEarlyAccess()}>Request early access
); } function DirectionA() { return (
Skip to main content
{window.ALoadedDayOne && } {window.ALiveDemos && }
{window.DemoModal && } {window.EarlyAccessModal && }
); } window.DirectionA = DirectionA;