// shared.jsx — content + small primitives. No "AI" terminology — uses // "OpenLines" for the product and "the voice / the agent" for the character. // Cross-Device foundation tokens injected here (standalone landing) for use // by buttons + components. Authoritative: dashboard/static/style.css "Cross-Device // Unified Experience — Foundation Tokens" block. Keep values in sync. // -------------------------------------------------------------------------- // CONTENT // -------------------------------------------------------------------------- const WF_CONTENT = { brand: "OpenLines", tagline: "Every call, answered.", taglineAlts: [ "Every call, answered.", "The voice that picks up when you can't.", "Pick up every call. Without picking up.", "Your after-hours, answered.", ], valueProp: "OpenLines is the voice that answers your business phone — after hours, during the rush, or any time a real person can't. It handles common questions, captures the details that need a human, and emails you a clean summary of every call.", primaryCta: { label: "Get started", href: "/get-started" }, secondaryCta: { label: "Email Lucas", href: "mailto:lucas@getopenlines.com" }, phone: "+1 (888) 561-7407", phoneHref: "tel:+18885617407", email: "lucas@getopenlines.com", steps: [ { n: "01", title: "A call comes in.", body: "A customer dials your business. OpenLines picks up — after hours, when you're swamped, or any time you've decided not to. The voice is warm, patient, and trained on your hours, services, and policies.", }, { n: "02", title: "OpenLines handles the conversation.", body: "The agent answers common questions from your business info. When the caller needs a real person, it captures their name, number, and reason for calling. No prodding, no upsell, no menu trees.", }, { n: "03", title: "You get a summary by email.", body: "A clean recap of every call — what was asked, what was handled, what needs follow-up. Read it with your coffee, or pull it up between appointments.", }, { n: "04", title: "You stay in control.", body: "Every word the voice says comes from you. During onboarding, we write the script together — your hours, your prices, your policies, your tone. Want to change how it answers a question? Send a note and it's updated.", }, ], doesnt: [ { title: "No outbound calls.", body: "Inbound only. No robocalls, no spam dials, no surprises on your customers." }, { title: "No call recording.", body: "The agent listens live and forgets. Nothing is stored or replayed." }, ], forWhoTitle: "Built for any business that picks up the phone.", forWhoBody: "After-hours coverage, rush-hour overflow, or your full front line — OpenLines scales from a single-location shop to a multi-location operation. Every business gets onboarded by a real person.", forWho: [ "Independent shops and family-run businesses", "Service businesses and trades", "Clinics, salons, and personal-care studios", "Growing teams across multiple locations", ], founder: { name: "Lucas Navallo", location: "Canby, Oregon", note: "Technology is finally good enough to take care of your customers without you sitting by the phone. OpenLines isn't here to replace the human touch — it's here so you can choose when to use it. Have it answer every call, only the ones after hours, or only the ones you can't get to. The decision stays yours.", }, }; // -------------------------------------------------------------------------- // PALETTE — cream paper, deep ink, muted oxblood accent // -------------------------------------------------------------------------- const PALETTE = { bg: "#f4ede1", // cream paper bg2: "#f9f4ea", // lighter paper for cards ink: "#241c12", // deep coffee sub: "rgba(36,28,18,0.62)", line: "rgba(36,28,18,0.18)", hairline: "rgba(36,28,18,0.28)", soft: "rgba(36,28,18,0.06)", accent: "#a04936", // muted oxblood / burnt sienna accentSoft: "rgba(160,73,54,0.12)", }; /* Cross-Device Unified Experience — Foundation Tokens (mirrored for standalone * public landing page at /landing). Authoritative source is the full comment * block + :root in dashboard/static/style.css (Workstream A). Values must * stay in sync; this is the temporary standalone copy (like intake.css). * Only primitives here — no branding/palette overrides. */ if (typeof document !== "undefined" && !document.getElementById("landing-foundation-tokens")) { const s = document.createElement("style"); s.id = "landing-foundation-tokens"; s.textContent = ` :root { --tap-min: 44px; /* hard minimum for any interactive control */ --tap-comfort: 48px; /* preferred for primary/frequent taps on touch */ --bp-small: 480px; --bp-mobile: 700px; --bp-tablet: 768px; --bp-tablet-landscape: 1024px; --focus-ring-width: 3px; --focus-ring: 0 0 0 var(--focus-ring-width) rgba(160,73,54,0.25); --safe-area-inset-top: env(safe-area-inset-top, 0px); --safe-area-inset-right: env(safe-area-inset-right, 0px); --safe-area-inset-bottom: env(safe-area-inset-bottom, 0px); --safe-area-inset-left: env(safe-area-inset-left, 0px); --space-1: 0.25rem; --space-2: 0.5rem; --space-3: 0.75rem; --space-4: 1rem; --space-5: 1.25rem; --space-6: 1.5rem; } `; document.head.appendChild(s); } // -------------------------------------------------------------------------- // PRIMITIVES // -------------------------------------------------------------------------- function ImagePlaceholder({ label, hint, style, tint, border, className }) { return (
{label}
{hint ?
{hint}
: null}
); } function SectionLabel({ children, style, accent = false }) { return (
{children}
); } function LiveDot({ color, size = 8, style }) { const c = color || PALETTE.accent; return (