import Link from "next/link"; import Subscribe from "@/components/Subscribe"; import { facilityToday } from "@/lib/compute"; import { PRICES } from "@/lib/plan"; import { plansLive } from "@/lib/plans-live"; /** Shared chrome for the public site, built to the design handoff (design/TC Nav, TC Footer). * Sticky nav on a 2px ink rule, four content links, no rounded corners anywhere. */ export const NAV = [ ["features", "Features", "/features"], ["how", "How it works", "/how-it-works"], ["reporting", "Reporting", "/reporting"], ["pricing", "Pricing", "/pricing"], // The guides are the likeliest way a linen services manager arrives here at all, so they get a // place in the main nav rather than being buried in the footer. ["guides", "Guides", "/guides"], ] as const; const FOOT = [ { h: "Product", links: [["Features", "/features"], ["How it works", "/how-it-works"], ["Reporting", "/reporting"], ["Pricing", "/pricing"], ["Roadmap", "/roadmap"], ["Open the demo", "/demo"]] }, { h: "Get going", links: [["Getting started", "/getting-started"], ["Guides", "/guides"], ["Support", "/support"], ["FAQ", "/faq"], ["Security & data", "/security"], ["Who built it", "/about"], ["Contact", "/contact"]] }, { h: "Legal", links: [["Privacy Policy", "/privacy"], ["Terms of Service", "/terms"], ["Data Security", "/data-security"], ["Acceptable Use", "/acceptable-use"], ["About & Contact", "/legal-about"], ["Delete Your Account", "/delete-account"]] }, ] as const; // ---- shared type styles, straight off the handoff's scale export const GUTTER = 40; export const wrap: React.CSSProperties = { maxWidth: 1440, margin: "0 auto", padding: `0 ${GUTTER}px`, width: "100%" }; export const kicker: React.CSSProperties = { fontSize: 12, letterSpacing: "0.15em", textTransform: "uppercase", fontWeight: 800, color: "var(--color-accent-700)" }; export const h1: React.CSSProperties = { fontFamily: "var(--font-heading)", fontWeight: 800, fontSize: "clamp(38px, 5.2vw, 78px)", lineHeight: 0.98, letterSpacing: "-0.035em", margin: 0, textWrap: "balance" as never }; export const h2: React.CSSProperties = { fontFamily: "var(--font-heading)", fontWeight: 800, fontSize: "clamp(28px, 3.4vw, 48px)", lineHeight: 1.02, letterSpacing: "-0.03em", margin: 0, textWrap: "balance" as never }; export const h3: React.CSSProperties = { fontFamily: "var(--font-heading)", fontWeight: 800, fontSize: "clamp(20px, 2.2vw, 28px)", letterSpacing: "-0.02em", margin: 0 }; export const body: React.CSSProperties = { fontSize: 16.5, lineHeight: 1.7, color: "var(--color-neutral-800)" }; export const small: React.CSSProperties = { fontSize: 13.5, lineHeight: 1.65, color: "var(--color-neutral-700)" }; export const dash = ; /** The 44/60px accent mark that sits under a heading. */ export const Rule = ({ w = 60, color = "var(--color-accent)" }: { w?: number; color?: string }) =>
; export function Wordmark({ size = 19, light = false }: { size?: number; light?: boolean }) { return ( ThreadCount ); } export function SiteNav({ current = "" }: { current?: string }) { return ( <> {/* The skip link in the site layout aims here. It has to be a real element after the nav rather than an id on the layout's wrapper, because every page renders its own SiteNav inside that wrapper so it can mark the active link — an id further out would sit above the nav and skip nothing. tabIndex −1 so following the fragment moves focus, not just the scroll position. */} > ); } /* The left cell of a two-column .tcm-split carries no left padding of its own: it sits on the page * gutter, so the wrap's 40px is already there. Below 900px globals.css collapses .tcm-split to one * column and that zero would become copy printed against the edge of the phone, so the same media * query in globals.css puts the gutter back. Put this class on the cell and give it * paddingTop/paddingRight/paddingBottom — never the four-value `padding` shorthand, which would set * padding-left: 0 inline and beat the rule. */ export const SPLIT_PAD = "tcm-splitpad"; /** Page header band. `tone` picks the three treatments the handoff uses for opening a page. */ export function PageHead({ kicker: k, title, lede, tone = "light", right, children }: { kicker: string; title: React.ReactNode; lede?: React.ReactNode; tone?: "light" | "ink" | "accent"; right?: React.ReactNode; children?: React.ReactNode; }) { const ink = tone === "ink", accent = tone === "accent"; // An accent band carries white body copy, so it is filled with --color-accent-600 rather than the // brand #ec3013: white on the brand red is 4.20:1, and the kicker and lede sat further under that // again in accent-300 and neutral-200. On accent-600, white clears 4.5:1 at every size used here. // #ec3013 stays the brand mark on rules, edge marks and the logo square, where it carries no text. const bg = ink ? "var(--color-text)" : accent ? "var(--color-accent-600)" : "var(--color-bg)"; const fg = ink || accent ? "#fff" : "var(--color-text)"; return ({lede}
} {children}{lede}