Clara. Healthcare landing page.

A calm, accessibility-first healthcare homepage in soft clinical whites with a sage/teal accent and rounded geometry. A breathing gradient orb sits behind a working appointment widget (visit type, day chips, a highlight pill that springs between time slots, a confirm button that morphs through a spinner into a drawn check). A sticky care-journey story morphs one illustration through four steps; a phone mock plays a clinician chat; outcome stats count up; a fictional insurer marquee, clinician cards with gentle lift, a monthly/annual pricing toggle with rolling prices and FAQ accordions round it out. AA+ contrast, 44px+ targets, full keyboard support and reduced-motion paths.

  • Breathing gradient orb
  • Spring time-slot picker
  • Sticky morphing storytelling
  • Count-up stats
  • Logo marquee
  • Pricing roll toggle
  • Accordion FAQ
Full screen

The preview is interactive — scroll inside it to run the animations.

The whole page. One file, nothing left out.

1,291 lines · 83.8 KB · imports React only. This is the exact file behind the preview above — every section, interaction and style.

  1. 01Save it as clara-health.tsx in any React 18+ project.
  2. 02Render <Page /> on a route of its own — it brings its own nav and footer.
  3. 03Swap the copy and tokens. Keep the DESIGN.md next to it so your AI agent follows the system.
clara-health.tsx
1/**
2 * Clara — modern primary care & virtual clinic.
3 *
4 * Calm, accessible healthcare homepage: soft clinical whites, a sage/teal
5 * accent, rounded geometry and a friendly rounded sans. Everything is
6 * procedural (CSS gradients + inline SVG) — no images, no web fonts.
7 *
8 * Motion
9 * - Breathing gradient orb behind the hero (8s inhale/exhale, follows the pointer).
10 * - Booking widget: visit-type switch, day chips, and a highlight pill that
11 * springs between time slots; confirm button morphs into a drawn check.
12 * - Sticky care-journey storytelling: 4 steps; the illustration is built from
13 * the same 7 primitives that morph between "phone", "video call",
14 * "care plan" and "follow-up chat".
15 * - Virtual-visit phone mock whose messages arrive in sequence.
16 * - Count-up outcome stats, insurance marquee, clinician cards with gentle
17 * lift, monthly/annual pricing toggle with rolling prices, FAQ accordions.
18 *
19 * Accessibility: AA+ contrast, 44px+ targets, real buttons/radios/regions,
20 * aria-live for booking + pricing, visible focus rings, reduced-motion paths
21 * (no orb breathing, no marquee, no count-up, instant morphs).
22 * SSR-safe: no window/document access during render.
23 */
24import { useEffect, useId, useRef, useState, type CSSProperties } from "react";
25
26/* ------------------------------------------------------------------ */
27/* Data */
28/* ------------------------------------------------------------------ */
29const DAYS = [
30 { key: "mon", dow: "Mon", date: "3", label: "Monday 3 November" },
31 { key: "tue", dow: "Tue", date: "4", label: "Tuesday 4 November" },
32 { key: "wed", dow: "Wed", date: "5", label: "Wednesday 5 November" },
33 { key: "thu", dow: "Thu", date: "6", label: "Thursday 6 November" },
34 { key: "fri", dow: "Fri", date: "7", label: "Friday 7 November" },
35];
36const TIMES = ["8:20", "9:00", "9:40", "10:40", "11:20", "1:00", "2:20", "3:40", "4:40"];
37/** Deterministic availability so SSR and client agree. */
38const isFree = (day: number, t: number) => ((day * 7 + t * 5) % 11) % 4 !== 0;
39
40const INSURERS = [
41 { name: "Bluefield Health", glyph: "leaf" },
42 { name: "Cascadia Mutual", glyph: "wave" },
43 { name: "Harbor & Pine", glyph: "pine" },
44 { name: "Meridian Care", glyph: "sun" },
45 { name: "Solace Assurance", glyph: "ring" },
46 { name: "Evergreen PPO", glyph: "leaf" },
47 { name: "Union Lakes", glyph: "wave" },
48 { name: "Atlas Benefit", glyph: "ring" },
49] as const;
50
51const SERVICES = [
52 { t: "Annual physicals", d: "A full hour with your clinician, labs drawn on site, results explained the same week.", icon: "heart", wide: true },
53 { t: "Chronic care", d: "Diabetes, hypertension and asthma plans with check-ins between visits.", icon: "pulse" },
54 { t: "Mental health", d: "Therapy and medication management, in person or by video.", icon: "mind" },
55 { t: "Women's health", d: "Contraception, menopause care and cervical screening.", icon: "flower" },
56 { t: "On-site labs", d: "Bloodwork in the room — most results within 24 hours.", icon: "drop" },
57 { t: "Same-day sick visits", d: "Colds, UTIs, rashes and sprains, usually seen within the hour.", icon: "clock", wide: true },
58] as const;
59
60const STEPS = [
61 { k: "Book", t: "Pick a time that fits your day", d: "See real availability across all six clinics and video. Most members book a same- or next-day visit in under a minute — no phone tree, no hold music." },
62 { k: "Meet", t: "Unhurried time with one clinician", d: "Visits are 30 minutes, not 7. Meet in person or on video with the same doctor who knows your history, your meds and how you like to be spoken to." },
63 { k: "Plan", t: "A plan you can actually follow", d: "Leave with a written care plan in the app: what we found, what we're testing, what to take, and what “better” should feel like by when." },
64 { k: "Follow up", t: "We check in, so you don't have to chase", d: "Your care team messages you after labs, before refills and at the milestones in your plan. Replies from a clinician within 2 hours, 7am–9pm." },
65];
66
67const STATS = [
68 { v: 4, suffix: " min", label: "Average wait in our waiting rooms", dec: 0 },
69 { v: 92, suffix: "%", label: "Members seen same or next day", dec: 0 },
70 { v: 4.9, suffix: "", label: "Average rating from 18,400 visit reviews", dec: 1 },
71 { v: 31, suffix: "%", label: "Fewer ER visits among chronic-care members", dec: 0 },
72];
73
74const CLINICIANS = [
75 { name: "Dr. Amara Osei", role: "Family medicine, MD", langs: "English, Twi, French", next: "Today 3:40 pm", skin: "#8B5A3C", hair: "#1E1410", style: "bun", coat: "#FFFFFF", bg: ["#DCEBE4", "#BFD8CB"] },
76 { name: "Dr. Lena Park", role: "Internal medicine, MD", langs: "English, Korean", next: "Tomorrow 9:00 am", skin: "#E8C2A0", hair: "#231A16", style: "bob", coat: "#2C6E67", bg: ["#E6EEF5", "#C9D9E8"] },
77 { name: "Mateo Ruiz, PA-C", role: "Urgent & sick visits", langs: "English, Spanish", next: "Today 1:00 pm", skin: "#C48A62", hair: "#3A2619", style: "short", coat: "#FFFFFF", bg: ["#F5EBDF", "#EBD6BF"] },
78 { name: "Dr. Ruth Adler", role: "Psychiatry, MD", langs: "English, German, Hebrew", next: "Thu 6 Nov, 10:40 am", skin: "#F0D2BC", hair: "#B9B4AE", style: "long", coat: "#3E5A7A", bg: ["#EEE8F3", "#D9CFE6"] },
79] as const;
80
81const PLANS = [
82 { name: "Pay per visit", monthly: 0, annual: 0, visit: "No membership fee", note: "We bill your insurance first; self-pay is $129.", feats: ["In-person & video visits", "On-site labs at cost", "Care plan in the app"], cta: "Book a visit" },
83 { name: "Clara Membership", monthly: 19, annual: 199, visit: "$0 primary-care visits", note: "Most members — it pays for itself at two visits.", feats: ["Unlimited primary-care visits", "Message a clinician 7am–9pm", "Same- or next-day booking", "Annual physical with full panel"], cta: "Start membership", featured: true },
84 { name: "Family", monthly: 49, annual: 499, visit: "Up to 5 people", note: "Kids 2+ and partners on one plan.", feats: ["Everything in Membership", "Pediatric well visits", "One shared family inbox"], cta: "Cover your family" },
85];
86
87const FAQ = [
88 { q: "Do you take my insurance?", a: "We're in network with most major PPO and HMO plans in Washington and Oregon, including all eight shown above. Without insurance, a visit is $129 and labs are billed at cost. Check your plan at booking — it takes about ten seconds." },
89 { q: "What does the membership actually cover?", a: "Unlimited primary-care visits in person or by video, messaging with your care team, and an annual physical with a full lab panel. Specialist referrals, imaging and prescriptions are billed to your insurance as usual." },
90 { q: "Can I keep the same clinician?", a: "Yes. You choose a primary clinician when you join and can switch at any time. When they're away, someone on the same small care team who has read your chart will see you." },
91 { q: "Is a video visit as good as coming in?", a: "For many things — follow-ups, medication changes, mental health, rashes, colds — yes. If your clinician needs to examine you or draw labs, they'll book an in-clinic slot that day at no extra cost." },
92 { q: "What if it's an emergency?", a: "Clara is not an emergency service. If you have chest pain, trouble breathing, signs of stroke or a severe injury, call 911 or go to the nearest emergency room." },
93];
94
95/* ------------------------------------------------------------------ */
96/* Small pieces */
97/* ------------------------------------------------------------------ */
98function Icon({ name }: { name: string }) {
99 const p = { fill: "none", stroke: "currentColor", strokeWidth: 1.7, strokeLinecap: "round" as const, strokeLinejoin: "round" as const };
100 let g = null;
101 if (name === "heart") g = <path {...p} d="M12 20s-7-4.4-7-10a4 4 0 0 1 7-2.6A4 4 0 0 1 19 10c0 5.6-7 10-7 10Z" />;
102 else if (name === "pulse") g = <path {...p} d="M3 12h4l2-5 4 10 2-5h6" />;
103 else if (name === "mind") g = <><path {...p} d="M8.5 19v-2.5A6.5 6.5 0 1 1 18 11c0 1.2.8 1.6 1.5 2.5L18 15v2a2 2 0 0 1-2 2h-1.5" /><path {...p} d="M10 9.5a2 2 0 1 1 3 1.7c-.6.4-1 .8-1 1.6" /></>;
104 else if (name === "flower") g = <><circle {...p} cx="12" cy="10" r="2.4" /><path {...p} d="M12 7.6C12 4 9 4 9 6s1.6 3 3 4c1.4-1 3-2 3-4s-3-2-3 1.6ZM9.6 10C6 10 6 13 8 13s3-1.6 4-3M14.4 10c3.6 0 3.6 3 1.6 3s-3-1.6-4-3M12 12.4V20" /></>;
105 else if (name === "drop") g = <path {...p} d="M12 3.5s6 6.4 6 10.5a6 6 0 0 1-12 0c0-4.1 6-10.5 6-10.5Z M9.5 14.5a2.5 2.5 0 0 0 2.5 2.5" />;
106 else if (name === "clock") g = <><circle {...p} cx="12" cy="12" r="8" /><path {...p} d="M12 7.5V12l3 2" /></>;
107 else if (name === "video") g = <><rect {...p} x="3" y="6.5" width="12.5" height="11" rx="3" /><path {...p} d="m15.5 10.5 5-3v9l-5-3" /></>;
108 else if (name === "pin") g = <><path {...p} d="M12 21s-6.5-5.6-6.5-11a6.5 6.5 0 0 1 13 0C18.5 15.4 12 21 12 21Z" /><circle {...p} cx="12" cy="10" r="2.3" /></>;
109 else if (name === "check") g = <path {...p} d="m5 12.5 4.5 4.5L19 7.5" />;
110 else if (name === "arrow") g = <path {...p} d="M5 12h14m-5-5 5 5-5 5" />;
111 else if (name === "plus") g = <path {...p} d="M12 5v14M5 12h14" />;
112 return (
113 <svg className="cl-ico" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
114 {g}
115 </svg>
116 );
117}
118
119function InsurerMark({ name, glyph }: { name: string; glyph: string }) {
120 const s = { fill: "none", stroke: "currentColor", strokeWidth: 1.8, strokeLinecap: "round" as const };
121 return (
122 <span className="cl-ins">
123 <svg viewBox="0 0 28 28" aria-hidden="true" focusable="false">
124 {glyph === "leaf" && <path {...s} d="M6 22C6 11 12 6 22 6c0 10-5 16-16 16Zm0 0 9-9" />}
125 {glyph === "wave" && <path {...s} d="M3 11c3-3 5-3 8 0s5 3 8 0 5-3 6-1M3 18c3-3 5-3 8 0s5 3 8 0 5-3 6-1" />}
126 {glyph === "pine" && <path {...s} d="M14 3 6 15h5l-4 6h14l-4-6h5L14 3Zm0 18v4" />}
127 {glyph === "sun" && <><circle {...s} cx="14" cy="14" r="5" /><path {...s} d="M14 3v3M14 22v3M3 14h3M22 14h3M6.2 6.2l2.1 2.1M19.7 19.7l2.1 2.1M6.2 21.8l2.1-2.1M19.7 8.3l2.1-2.1" /></>}
128 {glyph === "ring" && <><circle {...s} cx="11" cy="14" r="7" /><circle {...s} cx="17" cy="14" r="7" /></>}
129 </svg>
130 {name}
131 </span>
132 );
133}
134
135function Portrait({ c, i }: { c: (typeof CLINICIANS)[number]; i: number }) {
136 const id = "cl" + useId().replace(/[^a-zA-Z0-9_-]/g, "");
137 return (
138 <svg className="cl-portrait" viewBox="0 0 300 340" role="img" aria-label={`Illustrated portrait of ${c.name}`}>
139 <defs>
140 <linearGradient id={`${id}-bg`} x1="0" y1="0" x2="1" y2="1">
141 <stop offset="0" stopColor={c.bg[0]} />
142 <stop offset="1" stopColor={c.bg[1]} />
143 </linearGradient>
144 <radialGradient id={`${id}-face`} cx=".42" cy=".38" r=".7">
145 <stop offset="0" stopColor="#FFFFFF" stopOpacity=".22" />
146 <stop offset="1" stopColor="#000000" stopOpacity=".06" />
147 </radialGradient>
148 </defs>
149 <rect width="300" height="340" fill={`url(#${id}-bg)`} />
150 <circle cx={i % 2 ? 240 : 60} cy="70" r="90" fill="#FFFFFF" opacity=".35" />
151 {c.style === "long" && <path d="M92 150c-6 60 2 120 20 150h76c18-30 26-90 20-150-4-44-28-70-58-70s-54 26-58 70Z" fill={c.hair} />}
152 <path d="M40 340c4-62 46-96 110-96s106 34 110 96Z" fill={c.coat} />
153 {c.coat === "#FFFFFF" && <path d="M40 340c4-62 46-96 110-96s106 34 110 96Z" fill="none" stroke="#000" strokeOpacity=".06" strokeWidth="2" />}
154 <path d="M128 222h44v34c-6 10-38 10-44 0Z" fill={c.skin} />
155 <path d="M128 240c10 8 34 8 44 0v10c-10 8-34 8-44 0Z" fill="#000" opacity=".08" />
156 <path d="M118 256l32 46 32-46" fill="none" stroke={c.coat === "#FFFFFF" ? "#D9E4E0" : "#FFFFFF"} strokeOpacity=".7" strokeWidth="3" />
157 {i !== 3 && <path d="M112 262c-10 24-6 48 12 54m64-54c10 24 6 48-12 54" fill="none" stroke="#2B3A38" strokeWidth="3.5" strokeLinecap="round" />}
158 {i !== 3 && <circle cx="124" cy="318" r="7" fill="#9AA7A5" />}
159 <ellipse cx="150" cy="168" rx="50" ry="60" fill={c.skin} />
160 <ellipse cx="150" cy="168" rx="50" ry="60" fill={`url(#${id}-face)`} />
161 <ellipse cx="101" cy="172" rx="7" ry="11" fill={c.skin} />
162 <ellipse cx="199" cy="172" rx="7" ry="11" fill={c.skin} />
163 {c.style === "bun" && <><circle cx="150" cy="92" r="24" fill={c.hair} /><path d="M100 160c-2-40 20-62 50-62s52 22 50 62c-10-22-28-34-50-34s-40 12-50 34Z" fill={c.hair} /></>}
164 {c.style === "bob" && <path d="M96 196c-8-62 14-96 54-96s62 34 54 96c-4-4-8-28-10-44-22 4-56-2-74-22-6 18-14 40-24 66Z" fill={c.hair} />}
165 {c.style === "short" && <path d="M101 158c-4-38 18-60 49-60 30 0 52 18 50 56-8-14-18-24-28-28-20 8-48 8-62 4-4 8-6 18-9 28Z" fill={c.hair} />}
166 {c.style === "long" && <path d="M98 170c-4-48 18-74 52-74s56 26 52 74c-12-26-32-44-62-44-14 10-30 24-42 44Z" fill={c.hair} />}
167 </svg>
168 );
169}
170
171/** Morphing care-journey illustration: 7 primitives, 4 layouts. */
172type Prim = { x: number; y: number; w: number; h: number; r: number; bg: string; o?: number; rot?: number };
173const MORPH: Prim[][] = [
174 // 0 Book — phone with calendar rows and a chosen slot
175 [
176 { x: 150, y: 40, w: 220, h: 400, r: 36, bg: "#FFFFFF" },
177 { x: 178, y: 92, w: 164, h: 54, r: 14, bg: "#E4EFEA" },
178 { x: 178, y: 162, w: 76, h: 44, r: 22, bg: "#EEF2F1" },
179 { x: 266, y: 162, w: 76, h: 44, r: 22, bg: "#1F6F68" },
180 { x: 178, y: 220, w: 76, h: 44, r: 22, bg: "#EEF2F1" },
181 { x: 266, y: 220, w: 76, h: 44, r: 22, bg: "#EEF2F1" },
182 { x: 178, y: 370, w: 164, h: 46, r: 23, bg: "#0F2420" },
183 ],
184 // 1 Meet — video call: screen, clinician, self-view, controls
185 [
186 { x: 40, y: 80, w: 440, h: 300, r: 32, bg: "#FFFFFF" },
187 { x: 60, y: 100, w: 400, h: 220, r: 22, bg: "#DCEBE4" },
188 { x: 205, y: 140, w: 110, h: 110, r: 55, bg: "#C48A62" },
189 { x: 360, y: 230, w: 82, h: 72, r: 14, bg: "#1F6F68" },
190 { x: 205, y: 330, w: 40, h: 40, r: 20, bg: "#EEF2F1" },
191 { x: 275, y: 330, w: 40, h: 40, r: 20, bg: "#E26D5A" },
192 { x: 165, y: 236, w: 190, h: 84, r: 60, bg: "#2C6E67" },
193 ],
194 // 2 Plan — care-plan document with checklist
195 [
196 { x: 110, y: 40, w: 300, h: 400, r: 28, bg: "#FFFFFF" },
197 { x: 140, y: 72, w: 150, h: 22, r: 11, bg: "#0F2420" },
198 { x: 140, y: 130, w: 26, h: 26, r: 13, bg: "#1F6F68" },
199 { x: 180, y: 134, w: 190, h: 18, r: 9, bg: "#E4EFEA" },
200 { x: 140, y: 182, w: 26, h: 26, r: 13, bg: "#1F6F68" },
201 { x: 180, y: 186, w: 150, h: 18, r: 9, bg: "#E4EFEA" },
202 { x: 140, y: 250, w: 240, h: 150, r: 18, bg: "#F3E3D3" },
203 ],
204 // 3 Follow up — chat bubbles + heart
205 [
206 { x: 60, y: 60, w: 400, h: 360, r: 36, bg: "#FFFFFF" },
207 { x: 90, y: 96, w: 230, h: 60, r: 24, bg: "#EEF2F1" },
208 { x: 90, y: 176, w: 40, h: 40, r: 20, bg: "#C48A62" },
209 { x: 200, y: 176, w: 230, h: 60, r: 24, bg: "#1F6F68" },
210 { x: 90, y: 256, w: 180, h: 60, r: 24, bg: "#EEF2F1" },
211 { x: 90, y: 340, w: 58, h: 34, r: 17, bg: "#E4EFEA" },
212 { x: 340, y: 300, w: 80, h: 80, r: 40, bg: "#E26D5A" },
213 ],
214];
215
216/* ------------------------------------------------------------------ */
217/* Page */
218/* ------------------------------------------------------------------ */
219export function Page() {
220 const rootRef = useRef<HTMLDivElement>(null);
221 const slotsRef = useRef<HTMLDivElement>(null);
222 const [scrolled, setScrolled] = useState(false);
223 const [menuOpen, setMenuOpen] = useState(false);
224 const [mode, setMode] = useState<"clinic" | "video">("clinic");
225 const [day, setDay] = useState(0);
226 const [slot, setSlot] = useState<number | null>(3);
227 const [pill, setPill] = useState<{ x: number; y: number; w: number; h: number } | null>(null);
228 const [booking, setBooking] = useState<"idle" | "busy" | "done">("idle");
229 const [step, setStep] = useState(0);
230 const [annual, setAnnual] = useState(false);
231 const [open, setOpen] = useState<number | null>(0);
232 const [counts, setCounts] = useState<number[]>(STATS.map((s) => s.v));
233 const [chatIn, setChatIn] = useState(false);
234
235 const slotFree = (t: number) => isFree(day, t) && !(mode === "video" && t === 0);
236 const slotLabel = slot !== null ? `${TIMES[slot]} ${slot < 5 ? "am" : "pm"}` : "";
237 const firstFree = TIMES.findIndex((_, t) => slotFree(t));
238
239 // Move the highlight pill onto the selected slot (measured, so it works at any width).
240 useEffect(() => {
241 const grid = slotsRef.current;
242 if (!grid || slot === null) {
243 setPill(null);
244 return;
245 }
246 const measure = () => {
247 const b = grid.querySelector<HTMLElement>(`[data-slot="${slot}"]`);
248 if (b) setPill({ x: b.offsetLeft, y: b.offsetTop, w: b.offsetWidth, h: b.offsetHeight });
249 };
250 measure();
251 const win = grid.ownerDocument.defaultView;
252 const ro = win && "ResizeObserver" in win ? new win.ResizeObserver(measure) : null;
253 ro?.observe(grid);
254 return () => ro?.disconnect();
255 }, [slot, day, mode]);
256
257 // Keep the selection valid when day/mode changes.
258 useEffect(() => {
259 if (slot !== null && !slotFree(slot)) setSlot(firstFree >= 0 ? firstFree : null);
260 setBooking("idle");
261 // eslint-disable-next-line react-hooks/exhaustive-deps
262 }, [day, mode]);
263
264 // Scroll: nav state + active story step + pointer-follow orb + reveals + counters + chat.
265 useEffect(() => {
266 const root = rootRef.current;
267 if (!root) return;
268 const doc = root.ownerDocument;
269 const win = doc.defaultView;
270 if (!win) return;
271 const reduce = win.matchMedia("(prefers-reduced-motion: reduce)").matches;
272 root.classList.add("cl-js");
273
274 // Hosts that set overflow:hidden on BOTH <html> and <body> turn <body> into a
275 // scroll container that never scrolls, which silently breaks position:sticky.
276 // `clip` looks identical without that side effect; restored on unmount.
277 let restoreBody: (() => void) | null = null;
278 const fixRaf = win.requestAnimationFrame(() => {
279 const b = doc.body;
280 const bs = win.getComputedStyle(b);
281 const hs = win.getComputedStyle(doc.documentElement);
282 if (hs.overflowX !== "visible" && (bs.overflowX === "hidden" || bs.overflowY === "auto")) {
283 const prev = [b.style.overflowX, b.style.overflowY];
284 b.style.overflowX = "clip";
285 b.style.overflowY = "visible";
286 restoreBody = () => {
287 b.style.overflowX = prev[0];
288 b.style.overflowY = prev[1];
289 };
290 }
291 });
292
293 const steps = Array.from(root.querySelectorAll<HTMLElement>("[data-cl-step]"));
294 let raf = 0;
295 let lastS = false;
296 let lastStep = -1;
297 const update = () => {
298 raf = 0;
299 const y = win.scrollY || doc.documentElement.scrollTop;
300 const s = y > 16;
301 if (s !== lastS) {
302 lastS = s;
303 setScrolled(s);
304 }
305 const mid = win.innerHeight * 0.55;
306 let best = 0;
307 let bestD = Infinity;
308 steps.forEach((el, i) => {
309 const r = el.getBoundingClientRect();
310 const d = Math.abs(r.top + r.height / 2 - mid);
311 if (d < bestD) {
312 bestD = d;
313 best = i;
314 }
315 });
316 if (best !== lastStep) {
317 lastStep = best;
318 setStep(best);
319 }
320 };
321 const onScroll = () => {
322 if (!raf) raf = win.requestAnimationFrame(update);
323 };
324 update();
325 win.addEventListener("scroll", onScroll, { passive: true });
326 win.addEventListener("resize", onScroll);
327
328 // Orb follows the pointer with a critically-damped lerp.
329 const orb = root.querySelector<HTMLElement>(".cl-orb");
330 let tx = 0, ty = 0, cx = 0, cy = 0, orbRaf = 0;
331 const tick = () => {
332 cx += (tx - cx) * 0.06;
333 cy += (ty - cy) * 0.06;
334 orb?.style.setProperty("--mx", cx.toFixed(2));
335 orb?.style.setProperty("--my", cy.toFixed(2));
336 orbRaf = Math.abs(tx - cx) + Math.abs(ty - cy) > 0.01 ? win.requestAnimationFrame(tick) : 0;
337 };
338 const onMove = (e: PointerEvent) => {
339 if (reduce || e.pointerType !== "mouse") return;
340 tx = (e.clientX / win.innerWidth - 0.5) * 2;
341 ty = (e.clientY / win.innerHeight - 0.5) * 2;
342 if (!orbRaf) orbRaf = win.requestAnimationFrame(tick);
343 };
344 win.addEventListener("pointermove", onMove, { passive: true });
345
346 // Reveals.
347 const io = new win.IntersectionObserver(
348 (es) => es.forEach((e) => {
349 if (e.isIntersecting) {
350 e.target.classList.add("is-in");
351 io.unobserve(e.target);
352 }
353 }),
354 { rootMargin: "0px 0px -10% 0px", threshold: 0.15 },
355 );
356 root.querySelectorAll("[data-cl-rv]").forEach((el) => io.observe(el));
357
358 // Count-up stats (armed only when JS runs and motion is allowed).
359 const statsEl = root.querySelector(".cl-stats");
360 let countRaf = 0;
361 let sio: IntersectionObserver | null = null;
362 if (!reduce && statsEl) {
363 setCounts(STATS.map(() => 0));
364 sio = new win.IntersectionObserver(
365 (es) => {
366 if (!es[0].isIntersecting) return;
367 sio?.disconnect();
368 const t0 = win.performance.now();
369 const run = (now: number) => {
370 const k = Math.min(1, (now - t0) / 1800);
371 const e = 1 - Math.pow(1 - k, 4);
372 setCounts(STATS.map((s) => s.v * e));
373 if (k < 1) countRaf = win.requestAnimationFrame(run);
374 };
375 countRaf = win.requestAnimationFrame(run);
376 },
377 { threshold: 0.4 },
378 );
379 sio.observe(statsEl);
380 }
381
382 // Chat mock plays when visible.
383 const phone = root.querySelector(".cl-phone");
384 const pio = new win.IntersectionObserver(
385 (es) => {
386 if (es[0].isIntersecting) {
387 setChatIn(true);
388 pio.disconnect();
389 }
390 },
391 { threshold: 0.35 },
392 );
393 if (phone) pio.observe(phone);
394
395 return () => {
396 win.cancelAnimationFrame(fixRaf);
397 restoreBody?.();
398 if (raf) win.cancelAnimationFrame(raf);
399 if (orbRaf) win.cancelAnimationFrame(orbRaf);
400 if (countRaf) win.cancelAnimationFrame(countRaf);
401 win.removeEventListener("scroll", onScroll);
402 win.removeEventListener("resize", onScroll);
403 win.removeEventListener("pointermove", onMove);
404 io.disconnect();
405 sio?.disconnect();
406 pio.disconnect();
407 root.classList.remove("cl-js");
408 };
409 }, []);
410
411 const book = () => {
412 if (slot === null || booking !== "idle") return;
413 setBooking("busy");
414 const win = rootRef.current?.ownerDocument.defaultView;
415 win?.setTimeout(() => setBooking("done"), 900);
416 };
417
418 const onSlotKey = (e: React.KeyboardEvent<HTMLDivElement>) => {
419 if (!["ArrowRight", "ArrowLeft", "ArrowDown", "ArrowUp"].includes(e.key)) return;
420 e.preventDefault();
421 const free = TIMES.map((_, t) => t).filter(slotFree);
422 const cur = Math.max(0, free.indexOf(slot ?? free[0]));
423 const delta = e.key === "ArrowRight" ? 1 : e.key === "ArrowLeft" ? -1 : e.key === "ArrowDown" ? 3 : -3;
424 const next = free[Math.min(free.length - 1, Math.max(0, cur + delta))];
425 setSlot(next);
426 setBooking("idle");
427 const btn = slotsRef.current?.querySelector<HTMLElement>(`[data-slot="${next}"]`);
428 btn?.focus();
429 };
430
431 const nav = ["Services", "How it works", "Clinicians", "Pricing", "FAQ"];
432 const navId = (l: string) => "cl-" + l.toLowerCase().replace(/\s/g, "-");
433
434 return (
435 <div ref={rootRef} className="cl">
436 <style>{css}</style>
437 <a className="cl-skip" href="#cl-main">Skip to content</a>
438
439 {/* NAV */}
440 <header className={`cl-nav${scrolled ? " is-scrolled" : ""}${menuOpen ? " is-open" : ""}`}>
441 <div className="cl-nav-in">
442 <a href="#cl-top" className="cl-logo" aria-label="Clara Health — home">
443 <span className="cl-logo-mark" aria-hidden="true"><i /><i /></span>
444 clara
445 </a>
446 <nav className="cl-nav-links" aria-label="Primary">
447 {nav.map((l) => <a key={l} href={`#${navId(l)}`}>{l}</a>)}
448 </nav>
449 <div className="cl-nav-end">
450 <a href="#cl-top" className="cl-nav-sign">Sign in</a>
451 <a href="#cl-book" className="cl-btn cl-btn-primary cl-btn-sm">Book a visit</a>
452 <button type="button" className="cl-burger" aria-expanded={menuOpen} aria-label={menuOpen ? "Close menu" : "Open menu"} onClick={() => setMenuOpen((o) => !o)}>
453 <span /><span />
454 </button>
455 </div>
456 </div>
457 <div className="cl-sheet" hidden={!menuOpen}>
458 {nav.map((l) => <a key={l} href={`#${navId(l)}`} onClick={() => setMenuOpen(false)}>{l}</a>)}
459 <a href="#cl-top" onClick={() => setMenuOpen(false)}>Sign in</a>
460 </div>
461 </header>
462
463 <main id="cl-main">
464 {/* HERO */}
465 <section id="cl-top" className="cl-hero" aria-labelledby="cl-hero-title">
466 <div className="cl-orb" aria-hidden="true">
467 <span className="cl-orb-a" />
468 <span className="cl-orb-b" />
469 <span className="cl-orb-c" />
470 </div>
471 <div className="cl-wrap cl-hero-grid">
472 <div className="cl-hero-copy">
473 <p className="cl-badge"><span className="cl-dot" aria-hidden="true" /> Open today until 8 pm · 6 clinics in Seattle & Portland</p>
474 <h1 id="cl-hero-title" className="cl-h1">Primary care that has time for you.</h1>
475 <p className="cl-lede">Thirty-minute visits with a clinician who knows you — in clinic or on video, usually the same day. Most insurance accepted; $19 a month for everything else.</p>
476 <div className="cl-hero-ctas">
477 <a href="#cl-book" className="cl-btn cl-btn-primary">Find a time <Icon name="arrow" /></a>
478 <a href="#cl-pricing" className="cl-btn cl-btn-ghost">See membership</a>
479 </div>
480 <ul className="cl-hero-proof">
481 <li><strong>4.9</strong> from 18,400 reviews</li>
482 <li><strong>2 h</strong> clinician reply time</li>
483 <li><strong>In network</strong> with 40+ plans</li>
484 </ul>
485 </div>
486
487 {/* BOOKING WIDGET */}
488 <div id="cl-book" className="cl-widget" aria-labelledby="cl-widget-title">
489 <div className="cl-widget-head">
490 <h2 id="cl-widget-title" className="cl-widget-title">Book a visit</h2>
491 <div className="cl-seg" role="radiogroup" aria-label="Visit type">
492 {(["clinic", "video"] as const).map((m) => (
493 <button key={m} type="button" role="radio" aria-checked={mode === m} className={mode === m ? "is-on" : ""} onClick={() => setMode(m)}>
494 <Icon name={m === "clinic" ? "pin" : "video"} />
495 {m === "clinic" ? "In clinic" : "Video"}
496 </button>
497 ))}
498 <span className="cl-seg-thumb" style={{ transform: `translateX(${mode === "clinic" ? 0 : 100}%)` }} aria-hidden="true" />
499 </div>
500 </div>
501 <p className="cl-widget-where">{mode === "clinic" ? "Capitol Hill · 1520 E Pine St, Seattle" : "From anywhere in WA or OR · link sent by text"}</p>
502
503 <div className="cl-days" role="radiogroup" aria-label="Day">
504 {DAYS.map((d, i) => (
505 <button key={d.key} type="button" role="radio" aria-checked={day === i} aria-label={d.label} className={day === i ? "is-on" : ""} onClick={() => setDay(i)}>
506 <small>{d.dow}</small>
507 <span>{d.date}</span>
508 </button>
509 ))}
510 </div>
511
512 <div className="cl-slots" ref={slotsRef} role="radiogroup" aria-label={`Times on ${DAYS[day].label}`} onKeyDown={onSlotKey}>
513 <span className={`cl-slot-pill${pill ? " is-on" : ""}`} aria-hidden="true" style={pill ? ({ transform: `translate(${pill.x}px, ${pill.y}px)`, width: pill.w, height: pill.h } as CSSProperties) : undefined} />
514 {TIMES.map((t, i) => {
515 const free = slotFree(i);
516 const on = slot === i;
517 return (
518 <button key={t} type="button" role="radio" data-slot={i} aria-checked={on} tabIndex={on || (slot === null && i === firstFree) ? 0 : -1} disabled={!free} className={`cl-slot${on ? " is-on" : ""}`} onClick={() => { setSlot(i); setBooking("idle"); }}>
519 {t}<small>{i < 5 ? " am" : " pm"}</small>
520 </button>
521 );
522 })}
523 </div>
524
525 <div className="cl-widget-who">
526 <span className="cl-mini-av" aria-hidden="true" />
527 <p><strong>Dr. Amara Osei</strong><span>Family medicine · your primary clinician</span></p>
528 </div>
529
530 <button type="button" className={`cl-confirm is-${booking}`} onClick={book} disabled={slot === null} aria-describedby="cl-book-status">
531 <span className="cl-confirm-label">{slot === null ? "No times left — try another day" : `Book ${DAYS[day].dow} ${slotLabel}`}</span>
532 <span className="cl-confirm-spin" aria-hidden="true" />
533 <svg className="cl-confirm-check" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="m5 12.5 4.5 4.5L19 7.5" /></svg>
534 </button>
535 <p id="cl-book-status" className="cl-book-status" aria-live="polite">
536 {booking === "done"
537 ? `Booked: ${DAYS[day].label}, ${slotLabel}, ${mode === "clinic" ? "Capitol Hill" : "video"}. Confirmation sent by text.`
538 : "Free cancellation up to 2 hours before."}
539 </p>
540 </div>
541 </div>
542 </section>
543
544 {/* INSURANCE MARQUEE */}
545 <section className="cl-insurers" aria-labelledby="cl-ins-title">
546 <h2 id="cl-ins-title" className="cl-ins-title">In network with the plans most Washingtonians and Oregonians carry</h2>
547 <div className="cl-marquee">
548 <div className="cl-marquee-track">
549 {[0, 1].map((k) => (
550 <ul key={k} className="cl-marquee-set" aria-hidden={k === 1}>
551 {INSURERS.map((n) => <li key={n.name}><InsurerMark name={n.name} glyph={n.glyph} /></li>)}
552 </ul>
553 ))}
554 </div>
555 </div>
556 </section>
557
558 {/* SERVICES */}
559 <section id="cl-services" className="cl-services" aria-labelledby="cl-services-title">
560 <div className="cl-wrap">
561 <div className="cl-head" data-cl-rv>
562 <p className="cl-eyebrow">What we care for</p>
563 <h2 id="cl-services-title" className="cl-h2">Everything a good family doctor does — with the time to do it well.</h2>
564 </div>
565 <ul className="cl-bento">
566 {SERVICES.map((s, i) => (
567 <li key={s.t} className={`cl-card cl-svc${"wide" in s && s.wide ? " is-wide" : ""}`} data-cl-rv style={{ "--d": `${i * 60}ms` } as CSSProperties}>
568 <span className="cl-svc-ico"><Icon name={s.icon} /></span>
569 <h3 className="cl-h3">{s.t}</h3>
570 <p className="cl-body">{s.d}</p>
571 </li>
572 ))}
573 </ul>
574 </div>
575 </section>
576
577 {/* CARE JOURNEY */}
578 <section id="cl-how-it-works" className="cl-journey" aria-labelledby="cl-journey-title">
579 <div className="cl-wrap">
580 <div className="cl-head" data-cl-rv>
581 <p className="cl-eyebrow">How it works</p>
582 <h2 id="cl-journey-title" className="cl-h2">Four steps. One team the whole way.</h2>
583 </div>
584 <div className="cl-journey-grid">
585 <div className="cl-stage-wrap">
586 <div className="cl-stage" aria-hidden="true">
587 <div className="cl-stage-canvas">
588 {MORPH[step].map((p, i) => (
589 <span key={i} className={`cl-prim cl-prim-${i}`} style={{ transform: `translate(${p.x}px, ${p.y}px) rotate(${p.rot ?? 0}deg)`, width: p.w, height: p.h, borderRadius: p.r, background: p.bg, opacity: p.o ?? 1 } as CSSProperties} />
590 ))}
591 <span className={`cl-stage-tag${step === 1 ? " is-on" : ""}`} style={{ transform: "translate(78px, 116px)" }}>● Live · 12:04</span>
592 <span className={`cl-stage-tag${step === 2 ? " is-on" : ""}`} style={{ transform: "translate(158px, 282px)" }}>A1c target: 6.8 by March</span>
593 <span className={`cl-stage-tag${step === 3 ? " is-on" : ""}`} style={{ transform: "translate(104px, 112px)" }}>Your labs are back ��</span>
594 <span className={`cl-stage-tag cl-stage-tag-light${step === 0 ? " is-on" : ""}`} style={{ transform: "translate(200px, 381px)" }}>Confirm 10:40</span>
595 </div>
596 </div>
597 <ol className="cl-stage-rail">
598 {STEPS.map((s, i) => <li key={s.k} className={i <= step ? "is-on" : ""}><span>{s.k}</span></li>)}
599 </ol>
600 </div>
601 <ol className="cl-steps">
602 {STEPS.map((s, i) => (
603 <li key={s.k} data-cl-step className={`cl-step${step === i ? " is-on" : ""}`}>
604 <p className="cl-step-n">0{i + 1} · {s.k}</p>
605 <h3 className="cl-h3 cl-step-t">{s.t}</h3>
606 <p className="cl-body">{s.d}</p>
607 </li>
608 ))}
609 </ol>
610 </div>
611 </div>
612 </section>
613
614 {/* VIRTUAL CARE */}
615 <section className="cl-virtual" aria-labelledby="cl-virtual-title">
616 <div className="cl-wrap cl-virtual-grid">
617 <div data-cl-rv>
618 <p className="cl-eyebrow">Virtual clinic</p>
619 <h2 id="cl-virtual-title" className="cl-h2">A clinician in your pocket, 7am to 9pm.</h2>
620 <p className="cl-body cl-virtual-lede">Message your care team about anything — a new rash, a refill, a lab result you don't understand. A licensed clinician replies within two hours, and can move you to a video visit with one tap.</p>
621 <ul className="cl-ticks">
622 <li><Icon name="check" /> Prescriptions sent to any pharmacy</li>
623 <li><Icon name="check" /> Photos and lab results in one thread</li>
624 <li><Icon name="check" /> Notes written in plain language</li>
625 </ul>
626 </div>
627 <div className={`cl-phone${chatIn ? " is-in" : ""}`} role="img" aria-label="Clara app conversation: a member sends a photo of a rash, Dr. Lena Park replies and books a video visit for 4:40 pm.">
628 <div className="cl-phone-screen">
629 <div className="cl-phone-top">
630 <span className="cl-mini-av cl-mini-av-b" />
631 <p><strong>Dr. Lena Park</strong><span>Usually replies in 40 min</span></p>
632 </div>
633 <div className="cl-chat">
634 <p className="cl-msg cl-msg-me" style={{ "--d": "200ms" } as CSSProperties}>Hi! This rash on my forearm appeared yesterday and it's itchy. Should I worry?</p>
635 <div className="cl-msg cl-msg-me cl-msg-photo" style={{ "--d": "700ms" } as CSSProperties}><span /></div>
636 <p className="cl-typing" style={{ "--d": "1300ms" } as CSSProperties}><i /><i /><i /></p>
637 <p className="cl-msg cl-msg-dr" style={{ "--d": "2600ms" } as CSSProperties}>Thanks, Jordan — that looks like contact dermatitis, likely from the new detergent you mentioned. Nothing urgent.</p>
638 <p className="cl-msg cl-msg-dr" style={{ "--d": "3300ms" } as CSSProperties}>I'd like a quick look on video to be sure. I have 4:40 pm today?</p>
639 <p className="cl-chip-row" style={{ "--d": "3900ms" } as CSSProperties}><span className="cl-chip is-on">4:40 pm ✓</span><span className="cl-chip">Tomorrow</span></p>
640 </div>
641 </div>
642 </div>
643 </div>
644 </section>
645
646 {/* OUTCOMES */}
647 <section className="cl-outcomes" aria-labelledby="cl-outcomes-title">
648 <div className="cl-wrap">
649 <div className="cl-head cl-head-center" data-cl-rv>
650 <p className="cl-eyebrow">Outcomes, measured</p>
651 <h2 id="cl-outcomes-title" className="cl-h2">Care you can feel — and numbers we publish.</h2>
652 </div>
653 <dl className="cl-stats">
654 {STATS.map((s, i) => (
655 <div key={s.label} className="cl-stat">
656 <dt>{s.label}</dt>
657 <dd>
658 <span className="cl-stat-num">{counts[i].toFixed(s.dec)}</span>
659 <span className="cl-stat-suf">{s.suffix}</span>
660 </dd>
661 <span className="cl-stat-bar" style={{ "--k": String(counts[i] / s.v) } as CSSProperties} aria-hidden="true" />
662 </div>
663 ))}
664 </dl>
665 <p className="cl-footnote">Clara internal data, Jan–Sep 2026, across 41,200 members. ER comparison against matched non-member patients in the same ZIP codes.</p>
666 </div>
667 </section>
668
669 {/* CLINICIANS */}
670 <section id="cl-clinicians" className="cl-clinicians" aria-labelledby="cl-clin-title">
671 <div className="cl-wrap">
672 <div className="cl-head cl-head-row" data-cl-rv>
673 <div>
674 <p className="cl-eyebrow">Your care team</p>
675 <h2 id="cl-clin-title" className="cl-h2">Clinicians who stay.</h2>
676 </div>
677 <p className="cl-body cl-head-aside">Our clinicians average seven years with Clara and see 12 patients a day — about half the national average.</p>
678 </div>
679 <ul className="cl-clin-grid">
680 {CLINICIANS.map((c, i) => (
681 <li key={c.name} className="cl-clin" data-cl-rv style={{ "--d": `${i * 80}ms` } as CSSProperties}>
682 <a href="#cl-book" className="cl-clin-link">
683 <div className="cl-clin-img"><Portrait c={c} i={i} /></div>
684 <div className="cl-clin-body">
685 <h3 className="cl-clin-name">{c.name}</h3>
686 <p className="cl-clin-role">{c.role}</p>
687 <p className="cl-clin-langs">Speaks {c.langs}</p>
688 <p className="cl-clin-next"><span className="cl-dot" aria-hidden="true" /> Next: {c.next}</p>
689 </div>
690 </a>
691 </li>
692 ))}
693 </ul>
694 </div>
695 </section>
696
697 {/* PRICING */}
698 <section id="cl-pricing" className="cl-pricing" aria-labelledby="cl-pricing-title">
699 <div className="cl-wrap">
700 <div className="cl-head cl-head-center" data-cl-rv>
701 <p className="cl-eyebrow">Membership</p>
702 <h2 id="cl-pricing-title" className="cl-h2">Simple prices. No surprise bills.</h2>
703 <div className="cl-seg cl-seg-lg" role="radiogroup" aria-label="Billing period">
704 <button type="button" role="radio" aria-checked={!annual} className={!annual ? "is-on" : ""} onClick={() => setAnnual(false)}>Monthly</button>
705 <button type="button" role="radio" aria-checked={annual} className={annual ? "is-on" : ""} onClick={() => setAnnual(true)}>Annual <em>save 12%</em></button>
706 <span className="cl-seg-thumb" style={{ transform: `translateX(${annual ? 100 : 0}%)` }} aria-hidden="true" />
707 </div>
708 </div>
709 <ul className="cl-plans" aria-live="polite">
710 {PLANS.map((p, i) => {
711 const price = annual ? p.annual : p.monthly;
712 return (
713 <li key={p.name} className={`cl-plan${p.featured ? " is-featured" : ""}`} data-cl-rv style={{ "--d": `${i * 80}ms` } as CSSProperties}>
714 {p.featured && <span className="cl-plan-flag">Most popular</span>}
715 <h3 className="cl-plan-name">{p.name}</h3>
716 <p className="cl-plan-price">
717 <span className="cl-roll" key={`${p.name}-${annual}`}>{price === 0 ? "$129" : `$${price}`}</span>
718 <span className="cl-plan-per">{price === 0 ? "/ visit" : annual ? "/ year" : "/ month"}</span>
719 </p>
720 <p className="cl-plan-visit">{p.visit}</p>
721 <p className="cl-plan-note">{p.note}</p>
722 <ul className="cl-plan-feats">
723 {p.feats.map((f) => <li key={f}><Icon name="check" />{f}</li>)}
724 </ul>
725 <a href="#cl-book" className={`cl-btn ${p.featured ? "cl-btn-primary" : "cl-btn-soft"} cl-btn-block`}>{p.cta}</a>
726 </li>
727 );
728 })}
729 </ul>
730 <p className="cl-footnote cl-footnote-center">HSA/FSA eligible. Cancel any time from the app. Prices for Washington and Oregon residents, effective 1 January 2027.</p>
731 </div>
732 </section>
733
734 {/* FAQ */}
735 <section id="cl-faq" className="cl-faq" aria-labelledby="cl-faq-title">
736 <div className="cl-wrap cl-faq-grid">
737 <div data-cl-rv>
738 <p className="cl-eyebrow">Questions</p>
739 <h2 id="cl-faq-title" className="cl-h2">Good to know.</h2>
740 <p className="cl-body cl-faq-aside">Can't find an answer? Our member team picks up in under a minute: <a href="#cl-faq">(206) 555-0148</a>.</p>
741 </div>
742 <div className="cl-acc">
743 {FAQ.map((f, i) => {
744 const on = open === i;
745 return (
746 <div key={f.q} className={`cl-acc-item${on ? " is-open" : ""}`}>
747 <h3 className="cl-acc-h">
748 <button type="button" id={`cl-faq-b${i}`} aria-expanded={on} aria-controls={`cl-faq-p${i}`} onClick={() => setOpen(on ? null : i)}>
749 {f.q}
750 <span className="cl-acc-ico" aria-hidden="true"><Icon name="plus" /></span>
751 </button>
752 </h3>
753 <div id={`cl-faq-p${i}`} role="region" aria-labelledby={`cl-faq-b${i}`} className="cl-acc-panel" aria-hidden={!on}>
754 <div><p>{f.a}</p></div>
755 </div>
756 </div>
757 );
758 })}
759 </div>
760 </div>
761 </section>
762
763 {/* CTA */}
764 <section className="cl-cta" aria-labelledby="cl-cta-title">
765 <div className="cl-wrap">
766 <div className="cl-cta-card" data-cl-rv>
767 <span className="cl-cta-orb" aria-hidden="true" />
768 <h2 id="cl-cta-title" className="cl-h2 cl-cta-title">Your first visit could be this afternoon.</h2>
769 <p className="cl-cta-sub">14 open times across Seattle and Portland today.</p>
770 <a href="#cl-book" className="cl-btn cl-btn-light">Find a time <Icon name="arrow" /></a>
771 </div>
772 </div>
773 </section>
774 </main>
775
776 {/* FOOTER */}
777 <footer className="cl-foot">
778 <div className="cl-wrap">
779 <p className="cl-emergency"><strong>In an emergency, call 911.</strong> Clara provides primary and urgent care, not emergency care.</p>
780 <div className="cl-foot-grid">
781 <div className="cl-foot-brand">
782 <a href="#cl-top" className="cl-logo" aria-label="Clara Health — home"><span className="cl-logo-mark" aria-hidden="true"><i /><i /></span>clara</a>
783 <p>Primary care with time for you. Six clinics, one virtual clinic, about 41,000 members.</p>
784 </div>
785 <div>
786 <p className="cl-foot-h">Clinics</p>
787 <ul>
788 <li>Capitol Hill, Seattle</li>
789 <li>Ballard, Seattle</li>
790 <li>South Lake Union, Seattle</li>
791 <li>Pearl District, Portland</li>
792 <li>Alberta Arts, Portland</li>
793 <li>Bellevue Downtown</li>
794 </ul>
795 </div>
796 <div>
797 <p className="cl-foot-h">Care</p>
798 <ul>{["Primary care", "Urgent & sick visits", "Mental health", "Women's health", "Labs & vaccines"].map((x) => <li key={x}><a href="#cl-services">{x}</a></li>)}</ul>
799 </div>
800 <div>
801 <p className="cl-foot-h">Company</p>
802 <ul>{["About Clara", "Careers — we're hiring", "Clinician handbook", "Press", "Contact"].map((x) => <li key={x}><a href="#cl-top">{x}</a></li>)}</ul>
803 </div>
804 <div>
805 <p className="cl-foot-h">Hours</p>
806 <ul>
807 <li>Clinics Mon–Fri 7am–8pm</li>
808 <li>Sat–Sun 9am–5pm</li>
809 <li>Messaging daily 7am–9pm</li>
810 <li><a href="#cl-faq">(206) 555-0148</a></li>
811 </ul>
812 </div>
813 </div>
814 <div className="cl-foot-legal">
815 <span>© 2026 Clara Health PC. Clinical services provided by Clara Medical Group, PLLC.</span>
816 <span className="cl-foot-legal-links"><a href="#cl-top">Privacy</a><a href="#cl-top">HIPAA notice</a><a href="#cl-top">Accessibility</a><a href="#cl-top">Non-discrimination</a></span>
817 </div>
818 </div>
819 </footer>
820 </div>
821 );
822}
823
824export default Page;
825
826/* ------------------------------------------------------------------ */
827/* Styles */
828/* ------------------------------------------------------------------ */
829const css = String.raw`
830.cl {
831 --cl-canvas: #F6F8F7;
832 --cl-surface: #FFFFFF;
833 --cl-tint: #EAF2EE;
834 --cl-tint-2: #DCEBE4;
835 --cl-sage: #9DBFAE;
836 --cl-teal: #1F6F68;
837 --cl-teal-ink: #17544F;
838 --cl-ink: #0F2420;
839 --cl-ink-2: #465A55;
840 --cl-ink-3: #66776F;
841 --cl-line: #DFE7E3;
842 --cl-coral: #E26D5A;
843 --cl-peach: #F3D3BC;
844 --cl-font: ui-rounded, "SF Pro Rounded", ui-sans-serif, -apple-system, BlinkMacSystemFont, "SF Pro Text", "Inter", "Segoe UI", Roboto, sans-serif;
845 --cl-display: ui-rounded, "SF Pro Rounded", "SF Pro Display", ui-sans-serif, -apple-system, "Inter Display", "Inter", "Segoe UI", Roboto, sans-serif;
846 --fd-ease-out: cubic-bezier(0.22, 1, 0.36, 1);
847 --fd-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
848 --fd-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
849 --fd-dur-1: 120ms;
850 --fd-dur-2: 240ms;
851 --fd-dur-3: 420ms;
852 --fd-dur-4: 700ms;
853 --cl-r-card: 28px;
854 --cl-r-inner: 18px;
855 position: relative;
856 display: block;
857 width: 100%;
858 margin: 0;
859 padding: 0;
860 overflow-x: clip;
861 background: var(--cl-canvas);
862 color: var(--cl-ink);
863 font: 400 17px/1.6 var(--cl-font);
864 letter-spacing: 0;
865 text-align: left;
866 -webkit-font-smoothing: antialiased;
867 -moz-osx-font-smoothing: grayscale;
868}
869.cl *, .cl *::before, .cl *::after {
870 box-sizing: border-box;
871 margin: 0;
872 padding: 0;
873 border: 0 solid transparent;
874 font: inherit;
875 color: inherit;
876 letter-spacing: inherit;
877 text-decoration: none;
878 list-style: none;
879 background: none;
880 -webkit-appearance: none;
881 appearance: none;
882 min-width: 0;
883}
884.cl :where(svg) { display: block; }
885.cl :where(button) { cursor: pointer; text-align: inherit; line-height: inherit; }
886.cl :where(button:disabled) { cursor: not-allowed; }
887.cl :focus-visible { outline: 2px solid rgba(31, 111, 104, 0.7); outline-offset: 2px; border-radius: 12px; }
888.cl [hidden] { display: none !important; }
889.cl-skip { position: absolute; left: 16px; top: -60px; z-index: 100; padding: 12px 18px; border-radius: 9999px; background: var(--cl-ink); color: #FFFFFF; font: 600 15px/1 var(--cl-font); }
890.cl-skip:focus { top: 12px; }
891.cl-ico { width: 20px; height: 20px; flex: none; }
892.cl-wrap { width: 100%; max-width: 1200px; margin: 0 auto; padding: 0 32px; }
893
894.cl-eyebrow { font: 600 13px/1.4 var(--cl-font); letter-spacing: 0.06em; text-transform: uppercase; color: var(--cl-teal); }
895.cl-h1 { font: 600 clamp(44px, 5.6vw, 76px)/1.02 var(--cl-display); letter-spacing: -0.035em; color: var(--cl-ink); text-wrap: balance; }
896.cl-h2 { font: 600 clamp(32px, 3.6vw, 48px)/1.08 var(--cl-display); letter-spacing: -0.028em; color: var(--cl-ink); text-wrap: balance; }
897.cl-h3 { font: 600 20px/1.3 var(--cl-display); letter-spacing: -0.01em; color: var(--cl-ink); }
898.cl-body { font: 400 17px/1.6 var(--cl-font); color: var(--cl-ink-2); }
899.cl-head { max-width: 720px; margin-bottom: 56px; }
900.cl-head .cl-h2 { margin-top: 12px; }
901.cl-head-center { margin-left: auto; margin-right: auto; text-align: center; display: flex; flex-direction: column; align-items: center; }
902.cl-head-row { max-width: none; display: flex; align-items: flex-end; justify-content: space-between; gap: 40px; }
903.cl-head-aside { max-width: 380px; }
904.cl-footnote { margin-top: 28px; font: 400 13px/1.5 var(--cl-font); color: var(--cl-ink-3); }
905.cl-footnote-center { text-align: center; }
906.cl-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: #2E9B6A; box-shadow: 0 0 0 4px rgba(46, 155, 106, 0.18); flex: none; }
907
908/* Buttons */
909.cl-btn { display: inline-flex; align-items: center; justify-content: center; gap: 10px; min-height: 52px; padding: 0 26px; border-radius: 9999px; font: 600 16px/1 var(--cl-font); white-space: nowrap; transition: background-color var(--fd-dur-2) var(--fd-ease-out), box-shadow var(--fd-dur-2) var(--fd-ease-out), transform var(--fd-dur-2) var(--fd-spring), color var(--fd-dur-2) var(--fd-ease-out); }
910.cl-btn .cl-ico { width: 18px; height: 18px; transition: transform var(--fd-dur-2) var(--fd-ease-out); }
911.cl-btn:hover .cl-ico { transform: translateX(3px); }
912.cl-btn:active { transform: scale(0.96); transition-duration: var(--fd-dur-1); }
913.cl-btn-sm { min-height: 44px; padding: 0 20px; font-size: 15px; }
914.cl-btn-block { width: 100%; }
915.cl-btn-primary { background: var(--cl-teal); color: #FFFFFF; box-shadow: 0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 8px 20px -10px rgba(31, 111, 104, 0.7); }
916.cl-btn-primary:hover { background: var(--cl-teal-ink); }
917.cl-btn-ghost { background: rgba(255, 255, 255, 0.7); color: var(--cl-ink); box-shadow: inset 0 0 0 1.5px var(--cl-line); }
918.cl-btn-ghost:hover { background: #FFFFFF; box-shadow: inset 0 0 0 1.5px var(--cl-sage); }
919.cl-btn-soft { background: var(--cl-tint); color: var(--cl-teal-ink); }
920.cl-btn-soft:hover { background: var(--cl-tint-2); }
921.cl-btn-light { background: #FFFFFF; color: var(--cl-teal-ink); }
922.cl-btn-light:hover { background: var(--cl-tint); }
923
924/* NAV */
925.cl-nav { position: sticky; top: 0; z-index: 60; height: 84px; margin-bottom: -84px; padding: 12px 20px 0; pointer-events: none; }
926.cl-nav-in { pointer-events: auto; display: flex; align-items: center; justify-content: space-between; gap: 24px; max-width: 1200px; height: 60px; margin: 0 auto; padding: 0 12px 0 20px; border-radius: 9999px; transition: max-width var(--fd-dur-4) var(--fd-ease-out), background-color var(--fd-dur-3) var(--fd-ease-out), box-shadow var(--fd-dur-3) var(--fd-ease-out); }
927.cl-nav.is-scrolled .cl-nav-in { max-width: 1000px; background: rgba(255, 255, 255, 0.82); -webkit-backdrop-filter: saturate(1.6) blur(20px); backdrop-filter: saturate(1.6) blur(20px); box-shadow: 0 0 0 1px rgba(15, 36, 32, 0.06), 0 12px 32px -16px rgba(15, 36, 32, 0.25); }
928.cl-logo { display: inline-flex; align-items: center; gap: 10px; font: 700 24px/1 var(--cl-display); letter-spacing: -0.04em; color: var(--cl-ink); }
929.cl-logo-mark { position: relative; width: 28px; height: 28px; }
930.cl-logo-mark i { position: absolute; width: 18px; height: 18px; border-radius: 50%; }
931.cl-logo-mark i:first-child { left: 0; top: 5px; background: var(--cl-sage); }
932.cl-logo-mark i:last-child { right: 0; top: 5px; background: var(--cl-teal); mix-blend-mode: multiply; }
933.cl-nav-links { display: flex; gap: 6px; }
934.cl-nav-links a { display: inline-flex; align-items: center; min-height: 44px; padding: 0 14px; border-radius: 9999px; font: 500 15px/1 var(--cl-font); color: var(--cl-ink-2); transition: background-color var(--fd-dur-1) var(--fd-ease-out), color var(--fd-dur-1) var(--fd-ease-out); }
935.cl-nav-links a:hover { background: rgba(15, 36, 32, 0.05); color: var(--cl-ink); }
936.cl-nav-end { display: flex; align-items: center; gap: 6px; }
937.cl-nav-sign { display: inline-flex; align-items: center; min-height: 44px; padding: 0 14px; font: 500 15px/1 var(--cl-font); color: var(--cl-ink); border-radius: 9999px; }
938.cl-nav-sign:hover { background: rgba(15, 36, 32, 0.05); }
939.cl-burger { display: none; position: relative; width: 44px; height: 44px; border-radius: 50%; }
940.cl-burger span { position: absolute; left: 13px; right: 13px; height: 2px; border-radius: 2px; background: var(--cl-ink); transition: transform var(--fd-dur-3) var(--fd-ease-out); }
941.cl-burger span:first-child { top: 18px; }
942.cl-burger span:last-child { top: 25px; }
943.cl-nav.is-open .cl-burger span:first-child { transform: translateY(3.5px) rotate(45deg); }
944.cl-nav.is-open .cl-burger span:last-child { transform: translateY(-3.5px) rotate(-45deg); }
945.cl-sheet { pointer-events: auto; max-width: 1000px; margin: 8px auto 0; padding: 10px; border-radius: 24px; background: #FFFFFF; box-shadow: 0 0 0 1px rgba(15, 36, 32, 0.06), 0 24px 48px -20px rgba(15, 36, 32, 0.3); animation: cl-pop var(--fd-dur-3) var(--fd-ease-out); }
946.cl-sheet a { display: flex; align-items: center; min-height: 52px; padding: 0 16px; border-radius: 14px; font: 500 18px/1 var(--cl-font); color: var(--cl-ink); }
947.cl-sheet a:hover { background: var(--cl-tint); }
948@keyframes cl-pop { from { opacity: 0; transform: translateY(-6px) scale(0.98); } to { opacity: 1; transform: none; } }
949
950/* HERO */
951.cl-hero { position: relative; padding: 150px 0 110px; overflow: hidden; }
952.cl-orb { position: absolute; left: 50%; top: 46%; width: 1100px; height: 1100px; margin: -550px 0 0 -550px; pointer-events: none; --mx: 0; --my: 0; transform: translate3d(calc(var(--mx) * 24px), calc(var(--my) * 18px), 0); }
953.cl-orb span { position: absolute; inset: 0; border-radius: 50%; will-change: transform, opacity; }
954.cl-orb-a { background: radial-gradient(closest-side, rgba(157, 191, 174, 0.75), rgba(157, 191, 174, 0.28) 55%, rgba(157, 191, 174, 0) 100%); animation: cl-breathe 9s var(--fd-ease-in-out) infinite; }
955.cl-orb-b { inset: 18% 6% 8% 30% !important; background: radial-gradient(closest-side, rgba(243, 211, 188, 0.85), rgba(243, 211, 188, 0) 100%); animation: cl-breathe-b 9s var(--fd-ease-in-out) infinite; }
956.cl-orb-c { inset: 8% 34% 30% 4% !important; background: radial-gradient(closest-side, rgba(146, 205, 196, 0.7), rgba(146, 205, 196, 0) 100%); animation: cl-breathe-c 9s var(--fd-ease-in-out) infinite; }
957@keyframes cl-breathe { 0%, 100% { transform: scale(0.9); opacity: 0.75; } 45% { transform: scale(1.06); opacity: 1; } }
958@keyframes cl-breathe-b { 0%, 100% { transform: translate(-4%, 3%) scale(0.92); } 45% { transform: translate(3%, -2%) scale(1.08); } }
959@keyframes cl-breathe-c { 0%, 100% { transform: translate(3%, -2%) scale(1.04); } 45% { transform: translate(-3%, 4%) scale(0.9); } }
960.cl-hero-grid { position: relative; display: grid; grid-template-columns: 1.08fr 0.92fr; gap: 64px; align-items: center; }
961.cl-badge { display: inline-flex; align-items: center; gap: 10px; min-height: 36px; padding: 0 16px 0 14px; border-radius: 9999px; background: rgba(255, 255, 255, 0.75); box-shadow: inset 0 0 0 1px rgba(15, 36, 32, 0.07); font: 500 14px/1.3 var(--cl-font); color: var(--cl-ink-2); }
962.cl-hero-copy .cl-h1 { margin-top: 24px; max-width: 12ch; }
963.cl-lede { margin-top: 24px; max-width: 520px; font: 400 20px/1.55 var(--cl-font); color: var(--cl-ink-2); }
964.cl-hero-ctas { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 36px; }
965.cl-hero-proof { display: flex; flex-wrap: wrap; gap: 10px 28px; margin-top: 40px; font: 400 15px/1.4 var(--cl-font); color: var(--cl-ink-2); }
966.cl-hero-proof strong { font-weight: 700; color: var(--cl-ink); }
967.cl-hero-copy > * { animation: cl-up var(--fd-dur-4) var(--fd-ease-out) both; }
968.cl-hero-copy > *:nth-child(2) { animation-delay: 80ms; }
969.cl-hero-copy > *:nth-child(3) { animation-delay: 160ms; }
970.cl-hero-copy > *:nth-child(4) { animation-delay: 240ms; }
971.cl-hero-copy > *:nth-child(5) { animation-delay: 320ms; }
972.cl-widget { animation: cl-up 900ms var(--fd-ease-out) 200ms both; }
973@keyframes cl-up { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }
974
975/* Booking widget */
976.cl-widget { position: relative; padding: 28px; border-radius: 32px; background: rgba(255, 255, 255, 0.86); -webkit-backdrop-filter: saturate(1.4) blur(24px); backdrop-filter: saturate(1.4) blur(24px); box-shadow: 0 0 0 1px rgba(15, 36, 32, 0.06), 0 40px 80px -40px rgba(15, 36, 32, 0.35), 0 2px 6px rgba(15, 36, 32, 0.04); }
977.cl-widget-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
978.cl-widget-title { font: 600 24px/1.2 var(--cl-display); letter-spacing: -0.02em; color: var(--cl-ink); }
979.cl-widget-where { margin-top: 8px; font: 400 15px/1.4 var(--cl-font); color: var(--cl-ink-2); min-height: 21px; }
980.cl-seg { position: relative; display: inline-grid; grid-template-columns: 1fr 1fr; padding: 4px; border-radius: 9999px; background: var(--cl-tint); }
981.cl-seg button { position: relative; z-index: 1; display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: 40px; padding: 0 14px; border-radius: 9999px; font: 600 14px/1 var(--cl-font); color: var(--cl-ink-2); white-space: nowrap; transition: color var(--fd-dur-2) var(--fd-ease-out); }
982.cl-seg button .cl-ico { width: 16px; height: 16px; }
983.cl-seg button.is-on { color: var(--cl-teal-ink); }
984.cl-seg button em { font-style: normal; margin-left: 6px; padding: 3px 8px; border-radius: 9999px; background: #FFF1E8; color: #A3412F; font: 600 12px/1 var(--cl-font); }
985.cl-seg-thumb { position: absolute; top: 4px; bottom: 4px; left: 4px; width: calc(50% - 4px); border-radius: 9999px; background: #FFFFFF; box-shadow: 0 1px 2px rgba(15, 36, 32, 0.08), 0 6px 14px -6px rgba(15, 36, 32, 0.2); transition: transform var(--fd-dur-3) var(--fd-spring); }
986.cl-seg-lg { margin-top: 32px; }
987.cl-seg-lg button { min-height: 48px; padding: 0 22px; font-size: 16px; }
988.cl-days { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; margin-top: 22px; }
989.cl-days button { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; min-height: 64px; border-radius: 18px; background: var(--cl-canvas); box-shadow: inset 0 0 0 1px var(--cl-line); color: var(--cl-ink); transition: background-color var(--fd-dur-2) var(--fd-ease-out), box-shadow var(--fd-dur-2) var(--fd-ease-out), color var(--fd-dur-2) var(--fd-ease-out), transform var(--fd-dur-2) var(--fd-spring); }
990.cl-days button small { font: 500 12px/1 var(--cl-font); letter-spacing: 0.04em; text-transform: uppercase; color: var(--cl-ink-3); transition: color var(--fd-dur-2) var(--fd-ease-out); }
991.cl-days button span { font: 600 20px/1 var(--cl-display); }
992.cl-days button:hover { box-shadow: inset 0 0 0 1.5px var(--cl-sage); }
993.cl-days button:active { transform: scale(0.96); }
994.cl-days button.is-on { background: var(--cl-ink); color: #FFFFFF; box-shadow: none; }
995.cl-days button.is-on small { color: rgba(255, 255, 255, 0.75); }
996.cl-slots { position: relative; display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 14px; }
997.cl-slot-pill { position: absolute; left: 0; top: 0; z-index: 0; border-radius: 9999px; background: var(--cl-teal); box-shadow: 0 8px 18px -8px rgba(31, 111, 104, 0.8); opacity: 0; transition: transform 520ms var(--fd-spring), width var(--fd-dur-3) var(--fd-ease-out), height var(--fd-dur-3) var(--fd-ease-out), opacity var(--fd-dur-2) var(--fd-ease-out); }
998.cl-slot-pill.is-on { opacity: 1; }
999.cl-slot { position: relative; z-index: 1; min-height: 48px; border-radius: 9999px; box-shadow: inset 0 0 0 1px var(--cl-line); background: transparent; font: 600 16px/1 var(--cl-font); color: var(--cl-ink); text-align: center; font-variant-numeric: tabular-nums; transition: color var(--fd-dur-2) var(--fd-ease-out), box-shadow var(--fd-dur-2) var(--fd-ease-out), background-color var(--fd-dur-2) var(--fd-ease-out); }
1000.cl-slot small { font: 500 13px/1 var(--cl-font); color: var(--cl-ink-3); transition: color var(--fd-dur-2) var(--fd-ease-out); }
1001.cl-slot:hover:not(:disabled):not(.is-on) { background: var(--cl-tint); box-shadow: inset 0 0 0 1px var(--cl-sage); }
1002.cl-slot.is-on { color: #FFFFFF; box-shadow: none; }
1003.cl-slot.is-on small { color: rgba(255, 255, 255, 0.82); }
1004.cl-slot:disabled { color: #8C9A95; background: repeating-linear-gradient(-45deg, transparent 0 6px, rgba(15, 36, 32, 0.035) 6px 7px); text-decoration: line-through; text-decoration-color: rgba(15, 36, 32, 0.3); }
1005.cl-slot:disabled small { color: #8C9A95; }
1006.cl-widget-who { display: flex; align-items: center; gap: 12px; margin-top: 20px; padding: 12px; border-radius: 18px; background: var(--cl-canvas); }
1007.cl-widget-who p { display: flex; flex-direction: column; gap: 2px; font: 400 14px/1.3 var(--cl-font); color: var(--cl-ink-2); }
1008.cl-widget-who strong { font: 600 15px/1.3 var(--cl-font); color: var(--cl-ink); }
1009.cl-mini-av { width: 40px; height: 40px; flex: none; border-radius: 50%; background: radial-gradient(circle at 50% 38%, #8B5A3C 0 26%, transparent 27%), radial-gradient(circle at 50% 118%, #FFFFFF 0 46%, transparent 47%), linear-gradient(135deg, #DCEBE4, #BFD8CB); box-shadow: inset 0 0 0 1px rgba(15, 36, 32, 0.06); }
1010.cl-mini-av-b { background: radial-gradient(circle at 50% 38%, #E8C2A0 0 26%, transparent 27%), radial-gradient(circle at 50% 118%, #2C6E67 0 46%, transparent 47%), linear-gradient(135deg, #E6EEF5, #C9D9E8); }
1011.cl-confirm { position: relative; display: flex; align-items: center; justify-content: center; width: 100%; min-height: 56px; margin-top: 16px; border-radius: 9999px; background: var(--cl-ink); color: #FFFFFF; font: 600 17px/1 var(--cl-font); overflow: hidden; transition: background-color var(--fd-dur-3) var(--fd-ease-out), transform var(--fd-dur-2) var(--fd-spring); }
1012.cl-confirm:hover:not(:disabled) { background: #1E3A35; }
1013.cl-confirm:active:not(:disabled) { transform: scale(0.97); }
1014.cl-confirm:disabled { background: #C9D3CF; color: #3D4F4A; }
1015.cl-confirm-label { transition: opacity var(--fd-dur-2) var(--fd-ease-out), transform var(--fd-dur-3) var(--fd-ease-out); }
1016.cl-confirm-spin { position: absolute; left: 50%; top: 50%; width: 22px; height: 22px; margin: -11px 0 0 -11px; border-radius: 50%; border: 2.5px solid rgba(255, 255, 255, 0.3); border-top-color: #FFFFFF; opacity: 0; transition: opacity var(--fd-dur-2) var(--fd-ease-out); }
1017.cl-confirm-check { position: absolute; left: 50%; top: 50%; width: 28px; height: 28px; margin: -14px 0 0 -14px; fill: none; stroke: #FFFFFF; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 24; stroke-dashoffset: 24; }
1018.cl-confirm.is-busy .cl-confirm-label, .cl-confirm.is-done .cl-confirm-label { opacity: 0; transform: translateY(-10px); }
1019.cl-confirm.is-busy .cl-confirm-spin { opacity: 1; animation: cl-spin 700ms linear infinite; }
1020.cl-confirm.is-done { background: #2E8A5F; }
1021.cl-confirm.is-done .cl-confirm-check { stroke-dashoffset: 0; transition: stroke-dashoffset 520ms var(--fd-ease-out) 80ms; }
1022@keyframes cl-spin { to { transform: rotate(360deg); } }
1023.cl-book-status { margin-top: 12px; min-height: 42px; font: 400 14px/1.5 var(--cl-font); color: var(--cl-ink-2); text-align: center; }
1024
1025/* INSURERS */
1026.cl-insurers { padding: 24px 0 96px; }
1027.cl-ins-title { margin: 0 auto 28px; padding: 0 24px; max-width: 760px; font: 500 15px/1.5 var(--cl-font); color: var(--cl-ink-2); text-align: center; }
1028.cl-marquee { position: relative; overflow: hidden; -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent); mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent); }
1029.cl-marquee-track { display: flex; width: max-content; animation: cl-marquee 42s linear infinite; }
1030.cl-marquee:hover .cl-marquee-track { animation-play-state: paused; }
1031.cl-marquee-set { display: flex; gap: 56px; padding-right: 56px; }
1032.cl-ins { display: inline-flex; align-items: center; gap: 10px; min-height: 48px; font: 600 19px/1 var(--cl-display); letter-spacing: -0.015em; color: #52645E; white-space: nowrap; }
1033.cl-ins svg { width: 26px; height: 26px; color: var(--cl-teal); opacity: 0.85; }
1034@keyframes cl-marquee { to { transform: translateX(-50%); } }
1035
1036/* SERVICES */
1037.cl-services { padding: 40px 0 140px; }
1038.cl-bento { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
1039.cl-card { position: relative; padding: 28px; border-radius: var(--cl-r-card); background: var(--cl-surface); box-shadow: 0 0 0 1px rgba(15, 36, 32, 0.05), 0 1px 2px rgba(15, 36, 32, 0.04); }
1040.cl-svc { display: flex; flex-direction: column; gap: 10px; min-height: 240px; transition: transform var(--fd-dur-3) var(--fd-ease-out), box-shadow var(--fd-dur-3) var(--fd-ease-out); }
1041.cl-svc.is-wide { grid-column: span 2; }
1042.cl-svc:hover { transform: translateY(-4px); box-shadow: 0 0 0 1px rgba(15, 36, 32, 0.05), 0 24px 40px -24px rgba(15, 36, 32, 0.28); }
1043.cl-svc-ico { display: grid; place-items: center; width: 52px; height: 52px; margin-bottom: auto; border-radius: 16px; background: var(--cl-tint); color: var(--cl-teal); }
1044.cl-svc-ico .cl-ico { width: 26px; height: 26px; }
1045.cl-svc .cl-h3 { margin-top: 28px; }
1046.cl-svc .cl-body { font-size: 16px; }
1047.cl-svc.is-wide:first-child { background: linear-gradient(135deg, #FFFFFF 0%, #EAF2EE 100%); }
1048
1049/* REVEALS */
1050.cl-js [data-cl-rv] { opacity: 0; transform: translateY(22px); transition: opacity var(--fd-dur-4) var(--fd-ease-out), transform var(--fd-dur-4) var(--fd-ease-out), box-shadow var(--fd-dur-3) var(--fd-ease-out); transition-delay: var(--d, 0ms); }
1051.cl-js [data-cl-rv].is-in { opacity: 1; transform: none; }
1052.cl-js .cl-svc[data-cl-rv].is-in:hover, .cl-js .cl-clin[data-cl-rv].is-in:hover { transition-delay: 0ms; }
1053.cl-js .cl-svc[data-cl-rv].is-in:hover { transform: translateY(-4px); }
1054
1055/* JOURNEY */
1056.cl-journey { padding: 130px 0 120px; background: var(--cl-surface); border-radius: 48px 48px 0 0; }
1057.cl-journey-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 80px; }
1058.cl-stage-wrap { position: sticky; top: 110px; align-self: start; }
1059.cl-stage { position: relative; aspect-ratio: 1; border-radius: 40px; background: radial-gradient(circle at 30% 20%, #F3F8F5, #DCEBE4 70%); overflow: hidden; box-shadow: inset 0 0 0 1px rgba(15, 36, 32, 0.05); }
1060.cl-stage-canvas { position: absolute; left: 50%; top: 50%; width: 520px; height: 480px; margin: -240px 0 0 -260px; transform: scale(var(--cl-stage-s, 1)); contain: layout paint; }
1061.cl-prim { position: absolute; left: 0; top: 0; box-shadow: 0 1px 2px rgba(15, 36, 32, 0.06); transition: transform 760ms var(--fd-spring), width 620ms var(--fd-ease-out), height 620ms var(--fd-ease-out), border-radius 620ms var(--fd-ease-out), background-color 620ms var(--fd-ease-in-out), opacity var(--fd-dur-3) var(--fd-ease-out); }
1062.cl-prim-0 { box-shadow: 0 30px 60px -30px rgba(15, 36, 32, 0.35), 0 0 0 1px rgba(15, 36, 32, 0.05); }
1063.cl-prim-1 { transition-delay: 30ms; }
1064.cl-prim-2 { transition-delay: 60ms; }
1065.cl-prim-3 { transition-delay: 90ms; }
1066.cl-prim-4 { transition-delay: 120ms; }
1067.cl-prim-5 { transition-delay: 150ms; }
1068.cl-prim-6 { transition-delay: 180ms; }
1069.cl-stage-tag { position: absolute; left: 0; top: 0; padding: 7px 12px; border-radius: 9999px; background: #FFFFFF; box-shadow: 0 6px 16px -8px rgba(15, 36, 32, 0.4); font: 600 13px/1 var(--cl-font); color: var(--cl-ink); white-space: nowrap; opacity: 0; scale: 0.9; transition: opacity var(--fd-dur-3) var(--fd-ease-out), scale var(--fd-dur-3) var(--fd-spring); }
1070.cl-stage-tag-light { background: transparent; box-shadow: none; color: #FFFFFF; }
1071.cl-stage-tag.is-on { opacity: 1; scale: 1; transition-delay: 420ms; }
1072.cl-stage-rail { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-top: 20px; }
1073.cl-stage-rail li { position: relative; padding-top: 14px; font: 600 13px/1.2 var(--cl-font); color: var(--cl-ink-3); transition: color var(--fd-dur-3) var(--fd-ease-out); }
1074.cl-stage-rail li::before { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 4px; border-radius: 4px; background: var(--cl-line); }
1075.cl-stage-rail li::after { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 4px; border-radius: 4px; background: var(--cl-teal); transform: scaleX(0); transform-origin: left; transition: transform var(--fd-dur-4) var(--fd-ease-out); }
1076.cl-stage-rail li.is-on { color: var(--cl-teal-ink); }
1077.cl-stage-rail li.is-on::after { transform: scaleX(1); }
1078.cl-steps { padding: 6vh 0 18vh; }
1079.cl-step { display: flex; flex-direction: column; justify-content: center; min-height: 64vh; padding: 0 0 0 28px; border-left: 2px solid var(--cl-line); opacity: 0.38; transition: opacity var(--fd-dur-4) var(--fd-ease-out), border-color var(--fd-dur-4) var(--fd-ease-out); }
1080.cl-step.is-on { opacity: 1; border-left-color: var(--cl-teal); }
1081.cl-step-n { font: 600 14px/1 var(--cl-font); letter-spacing: 0.04em; text-transform: uppercase; color: var(--cl-teal); }
1082.cl-step-t { margin: 14px 0 14px; font-size: 32px; line-height: 1.15; letter-spacing: -0.025em; }
1083.cl-step .cl-body { max-width: 460px; font-size: 18px; }
1084
1085/* VIRTUAL */
1086.cl-virtual { padding: 120px 0; background: var(--cl-surface); }
1087.cl-virtual-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 80px; align-items: center; }
1088.cl-virtual .cl-h2 { margin-top: 12px; }
1089.cl-virtual-lede { margin-top: 22px; max-width: 480px; }
1090.cl-ticks { display: grid; gap: 12px; margin-top: 28px; }
1091.cl-ticks li { display: flex; align-items: center; gap: 12px; font: 500 17px/1.4 var(--cl-font); color: var(--cl-ink); }
1092.cl-ticks .cl-ico { width: 26px; height: 26px; padding: 4px; border-radius: 50%; background: var(--cl-tint); color: var(--cl-teal); }
1093.cl-phone { justify-self: center; width: 360px; padding: 12px; border-radius: 52px; background: #0F2420; box-shadow: 0 50px 100px -40px rgba(15, 36, 32, 0.55), inset 0 0 0 2px #2A3D39; }
1094.cl-phone-screen { height: 600px; border-radius: 42px; background: linear-gradient(180deg, #F6F8F7, #EEF4F1); overflow: hidden; }
1095.cl-phone-top { display: flex; align-items: center; gap: 12px; padding: 44px 20px 14px; background: rgba(255, 255, 255, 0.9); box-shadow: 0 1px 0 var(--cl-line); }
1096.cl-phone-top p { display: flex; flex-direction: column; gap: 2px; font: 400 13px/1.3 var(--cl-font); color: var(--cl-ink-2); }
1097.cl-phone-top strong { font: 600 15px/1.3 var(--cl-font); color: var(--cl-ink); }
1098.cl-chat { display: flex; flex-direction: column; gap: 10px; padding: 18px 16px; }
1099.cl-msg { max-width: 82%; padding: 11px 15px; border-radius: 20px; font: 400 15px/1.42 var(--cl-font); }
1100.cl-msg-me { align-self: flex-end; background: var(--cl-teal); color: #FFFFFF; border-bottom-right-radius: 6px; }
1101.cl-msg-dr { align-self: flex-start; background: #FFFFFF; color: var(--cl-ink); border-bottom-left-radius: 6px; box-shadow: 0 1px 2px rgba(15, 36, 32, 0.06); }
1102.cl-msg-photo { width: 150px; height: 104px; padding: 5px; }
1103.cl-msg-photo span { display: block; width: 100%; height: 100%; border-radius: 15px; background: radial-gradient(ellipse 30% 22% at 46% 52%, rgba(214, 104, 98, 0.75), transparent), radial-gradient(ellipse 18% 14% at 62% 40%, rgba(214, 104, 98, 0.55), transparent), linear-gradient(160deg, #E9C3A6, #D9A985); }
1104.cl-typing { align-self: flex-start; display: flex; gap: 4px; padding: 14px 16px; border-radius: 20px; background: #FFFFFF; }
1105.cl-typing i { width: 7px; height: 7px; border-radius: 50%; background: #9AA7A3; animation: cl-dots 1.2s var(--fd-ease-in-out) infinite; }
1106.cl-typing i:nth-child(2) { animation-delay: 150ms; }
1107.cl-typing i:nth-child(3) { animation-delay: 300ms; }
1108@keyframes cl-dots { 0%, 60%, 100% { transform: translateY(0); opacity: 0.5; } 30% { transform: translateY(-4px); opacity: 1; } }
1109.cl-chip-row { display: flex; gap: 8px; }
1110.cl-chip { display: inline-flex; align-items: center; min-height: 36px; padding: 0 14px; border-radius: 9999px; background: #FFFFFF; box-shadow: inset 0 0 0 1px var(--cl-line); font: 600 14px/1 var(--cl-font); color: var(--cl-ink); }
1111.cl-chip.is-on { background: var(--cl-ink); color: #FFFFFF; box-shadow: none; }
1112.cl-js .cl-chat > * { opacity: 0; transform: translateY(10px) scale(0.97); }
1113.cl-js .cl-phone.is-in .cl-chat > * { animation: cl-msg 520ms var(--fd-spring) var(--d) forwards; }
1114.cl-js .cl-phone.is-in .cl-typing { animation: cl-typing 1300ms var(--fd-ease-out) var(--d) forwards; }
1115@keyframes cl-msg { to { opacity: 1; transform: none; } }
1116@keyframes cl-typing { 0% { opacity: 0; transform: translateY(10px); max-height: 60px; } 15%, 80% { opacity: 1; transform: none; max-height: 60px; } 100% { opacity: 0; max-height: 0; padding-top: 0; padding-bottom: 0; margin-top: -10px; } }
1117
1118/* OUTCOMES */
1119.cl-outcomes { padding: 130px 0; }
1120.cl-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
1121.cl-stat { position: relative; display: flex; flex-direction: column-reverse; justify-content: flex-end; gap: 14px; padding: 28px; border-radius: var(--cl-r-card); background: var(--cl-surface); box-shadow: 0 0 0 1px rgba(15, 36, 32, 0.05); overflow: hidden; }
1122.cl-stat dt { font: 400 15px/1.45 var(--cl-font); color: var(--cl-ink-2); }
1123.cl-stat dd { display: flex; align-items: baseline; font: 600 64px/1 var(--cl-display); letter-spacing: -0.045em; color: var(--cl-ink); font-variant-numeric: tabular-nums; }
1124.cl-stat-suf { font-size: 32px; letter-spacing: -0.02em; color: var(--cl-teal); margin-left: 2px; }
1125.cl-stat-bar { position: absolute; left: 0; right: 0; bottom: 0; height: 4px; background: var(--cl-teal); transform: scaleX(var(--k)); transform-origin: left; }
1126
1127/* CLINICIANS */
1128.cl-clinicians { padding: 40px 0 130px; }
1129.cl-clin-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
1130.cl-clin-link { display: block; height: 100%; padding: 10px; border-radius: var(--cl-r-card); background: var(--cl-surface); box-shadow: 0 0 0 1px rgba(15, 36, 32, 0.05); transition: transform var(--fd-dur-3) var(--fd-ease-out), box-shadow var(--fd-dur-3) var(--fd-ease-out); }
1131.cl-clin-link:hover, .cl-clin-link:focus-visible { transform: translateY(-6px); box-shadow: 0 0 0 1px rgba(15, 36, 32, 0.05), 0 28px 44px -26px rgba(15, 36, 32, 0.35); }
1132.cl-clin-img { aspect-ratio: 300 / 340; border-radius: var(--cl-r-inner); overflow: hidden; }
1133.cl-portrait { width: 100%; height: 100%; transition: transform var(--fd-dur-4) var(--fd-ease-out); }
1134.cl-clin-link:hover .cl-portrait { transform: scale(1.04); }
1135.cl-clin-body { padding: 16px 10px 10px; }
1136.cl-clin-name { font: 600 19px/1.3 var(--cl-display); letter-spacing: -0.01em; color: var(--cl-ink); }
1137.cl-clin-role { margin-top: 2px; font: 500 15px/1.4 var(--cl-font); color: var(--cl-teal-ink); }
1138.cl-clin-langs { margin-top: 10px; font: 400 14px/1.4 var(--cl-font); color: var(--cl-ink-2); }
1139.cl-clin-next { display: flex; align-items: center; gap: 10px; margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--cl-line); font: 500 14px/1.3 var(--cl-font); color: var(--cl-ink); }
1140
1141/* PRICING */
1142.cl-pricing { padding: 130px 0; background: linear-gradient(180deg, var(--cl-canvas), #EDF4F0); }
1143.cl-plans { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; align-items: stretch; }
1144.cl-plan { position: relative; display: flex; flex-direction: column; padding: 32px; border-radius: 32px; background: var(--cl-surface); box-shadow: 0 0 0 1px rgba(15, 36, 32, 0.06); }
1145.cl-plan.is-featured { background: var(--cl-ink); color: #FFFFFF; box-shadow: 0 40px 80px -40px rgba(15, 36, 32, 0.6); }
1146.cl-plan-flag { position: absolute; top: 28px; right: 28px; padding: 6px 12px; border-radius: 9999px; background: var(--cl-sage); color: var(--cl-ink); font: 600 13px/1 var(--cl-font); }
1147.cl-plan-name { font: 600 20px/1.3 var(--cl-display); color: inherit; }
1148.cl-plan-price { display: flex; align-items: baseline; gap: 8px; height: 72px; margin-top: 18px; overflow: hidden; }
1149.cl-roll { display: inline-block; font: 600 64px/72px var(--cl-display); letter-spacing: -0.045em; animation: cl-roll var(--fd-dur-4) var(--fd-spring); font-variant-numeric: tabular-nums; }
1150@keyframes cl-roll { from { transform: translateY(70%); opacity: 0; } to { transform: none; opacity: 1; } }
1151.cl-plan-per { font: 500 16px/1 var(--cl-font); color: var(--cl-ink-2); }
1152.cl-plan.is-featured .cl-plan-per { color: rgba(255, 255, 255, 0.75); }
1153.cl-plan-visit { margin-top: 4px; font: 600 16px/1.4 var(--cl-font); color: var(--cl-teal-ink); }
1154.cl-plan.is-featured .cl-plan-visit { color: #A9D7C3; }
1155.cl-plan-note { margin-top: 6px; font: 400 15px/1.5 var(--cl-font); color: var(--cl-ink-2); }
1156.cl-plan.is-featured .cl-plan-note { color: rgba(255, 255, 255, 0.78); }
1157.cl-plan-feats { display: grid; gap: 12px; margin: 26px 0 30px; padding-top: 24px; border-top: 1px solid var(--cl-line); flex: 1; }
1158.cl-plan.is-featured .cl-plan-feats { border-top-color: rgba(255, 255, 255, 0.14); }
1159.cl-plan-feats li { display: flex; gap: 10px; font: 400 16px/1.4 var(--cl-font); }
1160.cl-plan-feats .cl-ico { width: 20px; height: 20px; color: var(--cl-teal); }
1161.cl-plan.is-featured .cl-plan-feats .cl-ico { color: #A9D7C3; }
1162
1163/* FAQ */
1164.cl-faq { padding: 130px 0; background: var(--cl-surface); }
1165.cl-faq-grid { display: grid; grid-template-columns: 0.8fr 1.2fr; gap: 80px; align-items: start; }
1166.cl-faq .cl-h2 { margin-top: 12px; }
1167.cl-faq-aside { margin-top: 18px; max-width: 340px; }
1168.cl-faq-aside a { color: var(--cl-teal-ink); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }
1169.cl-acc { border-top: 1px solid var(--cl-line); }
1170.cl-acc-item { border-bottom: 1px solid var(--cl-line); }
1171.cl-acc-h button { display: flex; align-items: center; justify-content: space-between; gap: 24px; width: 100%; min-height: 76px; padding: 18px 4px; font: 600 19px/1.35 var(--cl-display); letter-spacing: -0.01em; color: var(--cl-ink); }
1172.cl-acc-h button:hover { color: var(--cl-teal-ink); }
1173.cl-acc-ico { display: grid; place-items: center; width: 40px; height: 40px; flex: none; border-radius: 50%; background: var(--cl-tint); color: var(--cl-teal); transition: transform var(--fd-dur-3) var(--fd-spring), background-color var(--fd-dur-2) var(--fd-ease-out), color var(--fd-dur-2) var(--fd-ease-out); }
1174.cl-acc-item.is-open .cl-acc-ico { transform: rotate(45deg); background: var(--cl-teal); color: #FFFFFF; }
1175.cl-acc-panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--fd-dur-3) var(--fd-ease-out); }
1176.cl-acc-panel > div { overflow: hidden; }
1177.cl-acc-panel p { padding: 0 64px 26px 4px; font: 400 17px/1.65 var(--cl-font); color: var(--cl-ink-2); opacity: 0; transform: translateY(-6px); transition: opacity var(--fd-dur-3) var(--fd-ease-out), transform var(--fd-dur-3) var(--fd-ease-out); }
1178.cl-acc-item.is-open .cl-acc-panel { grid-template-rows: 1fr; }
1179.cl-acc-item.is-open .cl-acc-panel p { opacity: 1; transform: none; }
1180
1181/* CTA */
1182.cl-cta { padding: 0 0 110px; background: var(--cl-surface); }
1183.cl-cta-card { position: relative; display: flex; flex-direction: column; align-items: center; padding: 96px 32px; border-radius: 40px; background: var(--cl-teal); text-align: center; overflow: hidden; }
1184.cl-cta-orb { position: absolute; left: 50%; top: 50%; width: 900px; height: 900px; margin: -450px 0 0 -450px; border-radius: 50%; background: radial-gradient(closest-side, rgba(169, 215, 195, 0.55), rgba(169, 215, 195, 0)); animation: cl-breathe 9s var(--fd-ease-in-out) infinite; pointer-events: none; }
1185.cl-cta-title { position: relative; color: #FFFFFF; max-width: 680px; }
1186.cl-cta-sub { position: relative; margin: 16px 0 32px; font: 400 18px/1.5 var(--cl-font); color: rgba(255, 255, 255, 0.88); }
1187.cl-cta .cl-btn { position: relative; }
1188
1189/* FOOTER */
1190.cl-foot { padding: 64px 0 40px; background: var(--cl-ink); color: rgba(255, 255, 255, 0.82); }
1191.cl-emergency { padding: 18px 22px; border-radius: 18px; background: rgba(255, 255, 255, 0.06); font: 400 15px/1.5 var(--cl-font); color: rgba(255, 255, 255, 0.85); }
1192.cl-emergency strong { color: #FFFFFF; font-weight: 600; }
1193.cl-foot-grid { display: grid; grid-template-columns: 1.4fr repeat(4, 1fr); gap: 40px; padding: 56px 0; }
1194.cl-foot .cl-logo { color: #FFFFFF; }
1195.cl-foot .cl-logo-mark i:last-child { mix-blend-mode: normal; opacity: 0.9; }
1196.cl-foot-brand p { margin-top: 18px; max-width: 280px; font: 400 15px/1.6 var(--cl-font); color: rgba(255, 255, 255, 0.7); }
1197.cl-foot-h { margin-bottom: 16px; font: 600 13px/1 var(--cl-font); letter-spacing: 0.06em; text-transform: uppercase; color: #A9D7C3; }
1198.cl-foot-grid li { margin-bottom: 10px; font: 400 15px/1.45 var(--cl-font); color: rgba(255, 255, 255, 0.8); }
1199.cl-foot-grid a { color: rgba(255, 255, 255, 0.85); transition: color var(--fd-dur-1) var(--fd-ease-out); }
1200.cl-foot-grid a:hover { color: #FFFFFF; text-decoration: underline; text-underline-offset: 3px; }
1201.cl-foot-legal { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 16px; padding-top: 28px; border-top: 1px solid rgba(255, 255, 255, 0.12); font: 400 13px/1.5 var(--cl-font); color: rgba(255, 255, 255, 0.68); }
1202.cl-foot-legal-links { display: flex; flex-wrap: wrap; gap: 20px; }
1203.cl-foot-legal a { color: rgba(255, 255, 255, 0.8); }
1204.cl-foot-legal a:hover { color: #FFFFFF; }
1205
1206/* RESPONSIVE */
1207@media (max-width: 1080px) {
1208 .cl-nav-links { display: none; }
1209 .cl-burger { display: block; }
1210 .cl-hero-grid { grid-template-columns: 1fr; gap: 48px; }
1211 .cl-hero-copy .cl-h1 { max-width: 14ch; }
1212 .cl-widget { max-width: 560px; }
1213 .cl-bento { grid-template-columns: repeat(2, 1fr); }
1214 .cl-stats, .cl-clin-grid { grid-template-columns: repeat(2, 1fr); }
1215 .cl-foot-grid { grid-template-columns: repeat(3, 1fr); }
1216 .cl-foot-brand { grid-column: 1 / -1; }
1217 .cl-journey-grid { gap: 48px; }
1218 .cl-stage-canvas { --cl-stage-s: 0.82; }
1219}
1220@media (max-width: 860px) {
1221 .cl-wrap { padding: 0 20px; }
1222 .cl-nav { padding: 10px 12px 0; }
1223 .cl-nav-sign { display: none; }
1224 .cl-hero { padding: 120px 0 72px; }
1225 .cl-orb { top: 30%; width: 760px; height: 760px; margin: -380px 0 0 -380px; }
1226 .cl-lede { font-size: 18px; }
1227 .cl-widget { padding: 20px; border-radius: 28px; }
1228 .cl-widget-head { flex-direction: column; align-items: flex-start; gap: 12px; }
1229 .cl-widget-head .cl-seg { width: 100%; }
1230 .cl-head-row { flex-direction: column; align-items: flex-start; gap: 16px; }
1231 .cl-services, .cl-clinicians { padding-bottom: 96px; }
1232 .cl-svc { min-height: 0; }
1233 .cl-svc .cl-h3 { margin-top: 20px; }
1234 .cl-journey { padding: 96px 0 72px; border-radius: 32px 32px 0 0; }
1235 .cl-journey-grid { grid-template-columns: 1fr; gap: 0; }
1236 .cl-stage-wrap { top: 76px; z-index: 2; margin: 0 -20px; padding: 0 20px 16px; background: linear-gradient(180deg, #FFFFFF 85%, rgba(255, 255, 255, 0)); }
1237 .cl-stage { aspect-ratio: auto; height: 280px; border-radius: 28px; }
1238 .cl-stage-canvas { --cl-stage-s: 0.54; }
1239 .cl-stage-rail { margin-top: 12px; }
1240 .cl-steps { padding: 0 0 8vh; }
1241 .cl-step { min-height: 52vh; justify-content: flex-start; padding-top: 24px; }
1242 .cl-step-t { font-size: 26px; }
1243 .cl-step .cl-body { font-size: 17px; }
1244 .cl-virtual, .cl-outcomes, .cl-pricing, .cl-faq { padding: 96px 0; }
1245 .cl-virtual-grid, .cl-faq-grid { grid-template-columns: 1fr; gap: 56px; }
1246 .cl-plans { grid-template-columns: 1fr; }
1247 .cl-cta-card { padding: 72px 24px; border-radius: 32px; }
1248}
1249@media (max-width: 560px) {
1250 .cl-h1 { font-size: 44px; }
1251 .cl-bento, .cl-stats { grid-template-columns: 1fr; }
1252 .cl-svc.is-wide { grid-column: auto; }
1253 .cl-clin-grid { grid-template-columns: 1fr 1fr; gap: 10px; }
1254 .cl-clin-link { padding: 6px; border-radius: 22px; }
1255 .cl-clin-body { padding: 12px 6px 6px; }
1256 .cl-clin-name { font-size: 16px; }
1257 .cl-clin-role, .cl-clin-langs, .cl-clin-next { font-size: 13px; }
1258 .cl-stat dd { font-size: 56px; }
1259 .cl-days { gap: 6px; }
1260 .cl-days button { min-height: 60px; border-radius: 16px; }
1261 .cl-phone { width: 100%; max-width: 340px; }
1262 .cl-phone-screen { height: 560px; }
1263 .cl-acc-panel p { padding-right: 8px; }
1264 .cl-acc-h button { font-size: 17px; }
1265 .cl-foot-grid { grid-template-columns: 1fr 1fr; gap: 32px; }
1266 .cl-seg-lg button { padding: 0 14px; font-size: 15px; }
1267 .cl-plan { padding: 26px; }
1268}
1269
1270@media (prefers-reduced-motion: reduce) {
1271 .cl *, .cl *::before, .cl *::after { animation-duration: 1ms !important; animation-iteration-count: 1 !important; animation-delay: 0ms !important; transition-duration: 120ms !important; transition-delay: 0ms !important; }
1272 .cl-orb { transform: none; }
1273 .cl-orb span { animation: none !important; }
1274 .cl-marquee { -webkit-mask-image: none; mask-image: none; }
1275 .cl-marquee-track { animation: none !important; width: auto; justify-content: center; }
1276 .cl-marquee-set { flex-wrap: wrap; justify-content: center; gap: 16px 40px; padding: 0 20px; }
1277 .cl-marquee-set[aria-hidden="true"] { display: none; }
1278 .cl-js [data-cl-rv] { transform: none; }
1279 .cl-js .cl-chat > * { opacity: 1; transform: none; }
1280 .cl-js .cl-typing { display: none; }
1281}
1282@media (prefers-contrast: more) {
1283 .cl-widget, .cl-nav.is-scrolled .cl-nav-in { background: #FFFFFF; -webkit-backdrop-filter: none; backdrop-filter: none; box-shadow: 0 0 0 1px var(--cl-ink); }
1284 .cl-slot, .cl-days button { box-shadow: inset 0 0 0 1px var(--cl-ink-2); }
1285 .cl-card, .cl-plan, .cl-clin-link, .cl-stat { box-shadow: 0 0 0 1px var(--cl-ink-2); }
1286}
1287@media (prefers-reduced-transparency: reduce) {
1288 .cl-widget, .cl-nav.is-scrolled .cl-nav-in { background: #FFFFFF; -webkit-backdrop-filter: none; backdrop-filter: none; }
1289}
1290`;

The design brief. For you and your AI agent.

Tokens, type, layout, components and motion — written from the real values in the file, so a model can extend the page without drifting.

Clara — DESIGN.md

A modern primary-care and virtual clinic in Seattle and Portland. Calm and trustworthy: soft clinical whites, a sage / teal accent, rounded geometry, a friendly rounded sans, and accessibility first (AA+ contrast, 44px+ hit targets, full keyboard support).

All CSS lives in Page.tsx under the .cl prefix. Every element is reset explicitly, so the page is independent of host resets. Illustrations and portraits are inline SVG; there are no image files or web fonts.

1. Palette

RoleTokenHex
Page canvas--cl-canvas#F6F8F7
Cards, white bands--cl-surface#FFFFFF
Soft tint (icon tiles, segmented track)--cl-tint#EAF2EE
Tint 2 (hover)--cl-tint-2#DCEBE4
Sage (orb, logo, flags)--cl-sage#9DBFAE
Primary teal (CTA, selected slot)--cl-teal#1F6F68
Teal ink (hover, small teal text)--cl-teal-ink#17544F
Ink (headings, dark CTA, footer)--cl-ink#0F2420
Body text--cl-ink-2#465A55
Tertiary text--cl-ink-3#66776F
Hairlines--cl-line#DFE7E3
Warm accent (illustration only)--cl-coral / --cl-peach#E26D5A / #F3D3BC
Availability dot—#2E9B6A
Success (booked)—#2E8A5F
Light teal on ink—#A9D7C3

Contrast: ink on canvas is about 16:1, --cl-ink-2 on white about 7.3:1, --cl-ink-3 on white about 4.6:1, white on teal about 6:1, and disabled slot text #8C9A95 is decorative alongside a strikethrough plus a hatched fill.

2. Typography

  • Text (--cl-font): ui-rounded, "SF Pro Rounded", ui-sans-serif, -apple-system, BlinkMacSystemFont, "SF Pro Text", "Inter", "Segoe UI", Roboto, sans-serif
  • Display (--cl-display): ui-rounded, "SF Pro Rounded", "SF Pro Display", ui-sans-serif, -apple-system, "Inter Display", "Inter", …
StyleSpec
H1display 600, clamp(44px, 5.6vw, 76px) / 1.02, −0.035em, max 12ch
H2display 600, clamp(32px, 3.6vw, 48px) / 1.08, −0.028em
H3display 600, 20px / 1.3 (journey step titles 32px, −0.025em)
Stat numberdisplay 600, 64px, −0.045em, tabular; suffix 32px teal
Pricedisplay 600, 64px / 72px, −0.045em
Lede400, 20px / 1.55
Body400, 17px / 1.6
Eyebrow600, 13px, 0.06em, uppercase, teal
Button600, 16px (15px small)

3. Spacing, radii, elevation

  • Container 1200px with 32px side padding (20px at ≤860px). Section padding 120–140px (96px mobile).
  • Radii are concentric:
  • cards 28px (--cl-r-card), with the inner portrait at 18px (--cl-r-inner = 28 − 10 padding)
  • widget 32px, plans 32px
  • journey stage 40px; CTA band 40px
  • day chips 18px
  • all controls capsules (9999px).
  • Elevation is soft: a 1px ring rgba(15,36,32,.05) plus at most one long low shadow, e.g. widget 0 40px 80px -40px rgba(15,36,32,.35), hovered card 0 24px 40px -24px rgba(15,36,32,.28).
  • Hit targets: buttons 52px (44px small), slots 48px, day chips 64px, FAQ rows 76px, nav links 44px.

4. Motion tokens

--fd-ease-out    cubic-bezier(0.22, 1, 0.36, 1)
--fd-ease-in-out cubic-bezier(0.65, 0, 0.35, 1)
--fd-spring      cubic-bezier(0.34, 1.56, 0.64, 1)
--fd-dur-1 120ms · --fd-dur-2 240ms · --fd-dur-3 420ms · --fd-dur-4 700ms

Choreography

  1. Hero load. Copy children rise 18px in a 80ms stagger (700ms); the widget rises at 200ms.
  2. Breathing orb. Three radial blobs (sage, peach, aqua) "breathe" on a 9s in-out loop: the main one scales 0.9→1.06 and its opacity 0.75→1, and the others counter-drift. The whole orb follows the mouse at ±24/18px through a damped lerp (factor 0.06).
  3. Booking widget.
  4. The visit-type segmented thumb springs (420ms).
  5. The selected day chip turns ink.
  6. The slot highlight is one measured pill that springs between slots (transform 520ms with spring; width and height 420ms).
  7. Arrow keys move the selection across free slots (roving tabindex).
  8. Confirm morphs: label slides up and out → spinner (700ms rotation) → green fill with a check that draws (dash offset 520ms).
  9. Insurance marquee. Two identical sets translate −50% over 42s, with an edge mask; pauses on hover.
  10. Care journey (sticky storytelling). The left stage is position: sticky; top: 110px. The step nearest 55% of viewport height becomes active and steps fade from 0.38 to 1 opacity. The illustration is the same seven absolutely positioned primitives, re-laid out per step: transform (760ms spring), size and radius (620ms out), colour (620ms in-out), with a 30ms stagger per primitive. A caption chip pops in 420ms after each morph, and a 4-part progress rail fills.
  11. Virtual chat. When the phone is 35% visible, messages arrive in sequence (spring, delays 200–3900ms) and a typing indicator appears and collapses.
  12. Outcome stats. Count up from 0 over 1800ms (easeOutQuart) at 40% visibility; each card's 4px bottom bar scales with the value.
  13. Cards. Services lift 4px on hover; clinician cards lift 6px and the portrait zooms 1.04.
  14. Pricing toggle. Spring thumb; prices re-mount and roll up from 70% (700ms spring).
  15. FAQ. grid-template-rows: 0fr → 1fr (420ms); text fades and settles; the + rotates 45° with the spring and fills teal.
  16. Reveals. [data-cl-rv] fade up 22px (700ms) with per-item --d delays.

5. Sections

  1. Sticky nav. A floating capsule that gains white glass (blur(20px)), a ring and a shadow on scroll, and narrows from 1200 to 1000px.
  2. Hero. Badge, H1, lede, two CTAs, proof row, booking widget, breathing orb.
  3. Insurance marquee. Eight fictional insurers with line-glyph marks.
  4. Services bento. 4 columns, two wide cards.
  5. Care journey. Sticky morphing stage plus four steps.
  6. Virtual clinic. Copy, ticks, and an animated phone conversation.
  7. Outcomes. Four count-up stats plus a methodology footnote.
  8. Clinicians. Four illustrated portrait cards with languages and next availability.
  9. Pricing. Monthly/annual toggle and three plans (featured plan in ink).
  10. FAQ. Five accordions.
  11. CTA band. Teal with a breathing orb.
  12. Footer. Emergency notice, brand, clinics, care, company, hours, legal.

6. Components and states

  • Primary button. Teal, with an inner highlight and a teal glow shadow. Hover → teal-ink with the arrow nudged 3px; press scales to 0.96.
  • Ghost / soft / light buttons. Ghost is a 70%-white capsule with a 1.5px hairline (hover: white with a sage ring); soft uses the tint fill; light is white on teal.
  • Slot. States: idle (hairline), hover (tint plus sage ring), selected (white text over the moving teal pill), disabled (hatched, struck through, aria-disabled through native disabled).
  • Day chip. Idle canvas with a hairline; hover sage ring; selected ink.
  • Confirm. States idle | busy | done; disabled (no slots) is grey with a dark label; the status line is aria-live.
  • Accordion. Native button with aria-expanded and aria-controls; panel role="region".

7. Responsive

  • ≤1080px: burger menu, the hero stacks (widget max 560px), bento, stats and clinicians go 2-up, the stage canvas scales to 0.82.
  • ≤860px:
  • the widget header stacks
  • the journey becomes one column, with the stage sticky under the nav (280px tall, canvas scale 0.54, a white fade under it) and steps scrolling beneath
  • plans stack.
  • ≤560px: 44px H1, single-column bento and stats, 2-up compact clinician cards, full-width phone.

8. Accessibility and reduced motion

  • Skip link, landmarks, labelled sections, role="radiogroup"/radio for visit type, day, slot and billing. Arrow-key navigation in slots. Visible 2px teal focus ring at 70%.
  • prefers-reduced-motion:
  • no orb breathing or pointer follow
  • the marquee becomes a static wrapped list
  • no count-up (real values render immediately)
  • chat messages are visible with no typing indicator
  • transitions are capped at 120ms.
  • prefers-contrast: more: opaque surfaces with ink rings. prefers-reduced-transparency: opaque widget and nav.
  • Fixes the html+body overflow host quirk that breaks position: sticky, and restores it on unmount.

9. Do / Don't

  • Do keep one teal action per view and use ink for the confirm action.
  • Do keep radii concentric (outer − padding = inner) and controls capsule-shaped.
  • Do write numbers with units and sources; healthcare copy must be specific and calm.
  • Don't use red for anything but the illustration's "end call" and heart accents. Errors and emergencies use words, not colour alone.
  • Don't shrink targets below 44px or put body text below 13px.
  • Don't autoplay anything that carries essential information; the chat is illustrative and also described in its aria-label.