--- version: alpha name: cadence-clinic-design description: "A soft clinical-white canvas (#f5f8f7) with near-black ink (#16221e), a warm vital-signs coral (#c1442f) for the pulse line, and a calm teal (#33707a) for trust and confirmation. Sora carries every heading at weight 600; Source Sans 3 carries the surrounding prose. The system reads as an actual small practice's site, unhurried and plain-spoken, never a hospital-system portal full of alerts." --- ## Brand Positioning Cadence Clinic is for patients who want the same three people every visit and a plain sentence about what happens next, not a portal full of red badges and a different provider each time. It exists because most telehealth UI borrows hospital-system chrome (urgent-red notification counts, clinical jargon, a wall of intake fields) that makes a routine visit feel like an emergency. Cadence Clinic's bet: the one piece of drama on the page should be a heartbeat line settling into a calm resting rhythm, exactly what a good visit is supposed to do for you. Every visual decision below serves that one bet. If a component doesn't help someone book a visit or say what's going on in their own words, or doesn't come from a real care interaction (visit table, care-team card, intake form, status), it doesn't belong in this system. colors: accent: "#c1442f" accent-alt: "#33707a" ink: "#16221e" ink-muted: "#5c6b66" canvas: "#f5f8f7" surface: "#ffffff" surface-raised: "#eaf1ef" border: "#dce6e2" focus-ring: "#33707a" ## Logo / Wordmark System The mark is a rounded-square card containing a single EKG blip, a flat line with one heartbeat spike, literally the resting-state frame of the `.pulse` component the hero draws in on scroll. It is not a generic medical cross, it's a 1:1 miniature of the actual waveform. ```svg ``` - **Lockup**: mark, then a `0.5rem` gap, then the wordmark "Cadence Clinic" in Sora/600, mark always left of the wordmark, never stacked or reversed. - **Minimum size**: mark not smaller than `16px` square, below that the blip's spike flattens into noise. Wordmark not smaller than `0.85rem`. - **Clearspace**: empty margin equal to the mark's own height (22px at default size) on all four sides of the lockup. - **Don't**: recolor the mark for a dark background, it only ever renders on `--panel`/`--bg`. Don't use a medical cross or a generic caduceus, the brand's whole visual logic is the pulse line, not clinical iconography. Don't animate the mark itself, the drawing motion belongs to the hero's full-width pulse, not the nav. Don't stretch or rotate it. ## Color System ### Roles | Role | Token | Value | Used for | |---|---|---|---| | Canvas | `--bg` | `#f5f8f7` | Page background | | Surface | `--panel` | `#ffffff` | Panels, nav, cards, table body | | Surface raised | `--surface-raised` | `#eaf1ef` | Table header, row hover, input focus bg | | Border | `--border` | `#dce6e2` | Panel edges, dividers, input outlines, table rules | | Ink | `--ink` | `#16221e` | Primary text | | Ink muted | `--dim` | `#5c6b66` | Secondary text, helper text, placeholders, pending status | | Vital / pulse | `--accent` | `#c1442f` | Pulse line, live-dot, error-banner text, reschedule-needed state | | Trust / confirm | `--accent-alt` | `#33707a` | Primary button fill, confirmed status, care-team initials, focus ring | The coral is a "vital sign" color, not a danger color, it's what draws the eye on the pulse line and in the one state (reschedule) that needs a patient's attention, but it never fills a large surface, at this lightness a solid coral field would read as an alert banner, which is exactly the hospital-portal feeling the brand exists to avoid. The primary button intentionally fills with the calmer teal instead. ### Contrast (computed from the hex values above via WCAG relative luminance) | Pairing | Ratio | Passes | |---|---|---| | `--ink` text on `--bg` | ~15.3:1 | AAA | | `--dim` text on `--bg` | ~5.2:1 | AA (body text) | | `--accent` text on `--bg` (error copy, status word) | ~4.8:1 | AA (body text) | | `--accent-alt` text on `--bg` (confirmed status, links) | ~5.3:1 | AA (body text) | | White label on `--accent-alt` fill (primary button) | ~5.6:1 | AA | | `--ink` on `--panel` | ~16.4:1 | AAA | | `--ink` on `--surface-raised` | ~14.3:1 | AAA | The rule this table exists to enforce: `--accent` (coral) is tuned specifically to clear AA at body-text size on `--bg` so it can label the reschedule state directly, but it never becomes a button fill, at fill-strength neither white nor `--ink` labels clear AA against it, which is exactly why the primary action uses `--accent-alt` instead. typography: display: fontFamily: Sora fontWeight: 600 body: fontFamily: Source Sans 3 fontWeight: 400 ## Type Scale | Token | Size | Weight | Family | Line-height | Letter-spacing | Used for | |---|---|---|---|---|---|---| | `--text-display` | `clamp(2.25rem, 5.5vw, 3.75rem)` | 600 | Sora | 1.1 | 0 | Hero H1 only | | `--text-h2` | `1.5rem` (homepage) / `1.4rem` (second page) | 600 | Sora | 1.2 | 0 | Section headings | | `--text-body` | `1rem` | 400 | Source Sans 3 | 1.6 | 0 | Paragraph copy | | `--text-small` | `0.85rem` | 400 | Source Sans 3 | 1.5 | 0.01em | Footer, nav utility links, helper text | | `--text-status` | `0.85rem` | 600 | Source Sans 3 | 1.4 | 0 | Table status cells, badges | Rule for Sora vs. Source Sans 3: Sora is reserved for the hero headline and section headings, short moments meant to feel considered. Everything read for information, body copy, nav labels, button labels, table cells, form labels, is Source Sans 3, chosen specifically for how cleanly it sets at small sizes in a form or table, where a patient is actually reading, not just glancing. ## Spacing Scale Base unit `8px`. Tokens in use: `--space-2: 0.5rem`, `--space-3: 1rem`, `--space-4: 1.5rem`, `--space-6: 2rem`, `--space-8: 3rem`, `--space-12: 4rem`. ## Shape One radius scale: `--radius: 12px` for panels, cards, inputs, and table containers, slightly softer than a typical SaaS `10px` on purpose, it reads less clinical. `--radius-pill: 999px` for buttons and status badges. Care-team avatars are the one intentional full-circle exception. ## Iconography No icon library. The only non-text glyphs are the logo mark, the pulse-line waveform, and one Unicode glyph, `✓` (confirmed), set at the same weight as the status text beside it. Provider identity is communicated with initials in a circle, not a photo or an icon, matching the brand's "no stock imagery" stance. If a future page needs more iconography, the fallback is Phosphor (line style, `1.5` stroke), that decision just hasn't been needed yet. ## Motion Principles Scroll reveals a heartbeat line drawing itself across the hero, one continuous stroke from a flat baseline through a single beat and back to flat, exactly once, the moment the pulse section enters view. Once the line finishes drawing, a small dot at its resting point begins a slow, subtle scale-and-fade loop, a "this is live" signal, not a demand for attention. That loop is the only continuously-repeating animation anywhere in the system, and it stops entirely under `prefers-reduced-motion`, along with the draw-in itself rendering fully resolved with no animation. Everything else (buttons, inputs, table rows, care-team cards) uses only `:hover`/`:active`/`:focus-visible` CSS transitions. ### Never-list (written before the impeccable-pass rebuild) 1. No centered marketing headline sitting above the pulse line, the pulse panel is the hero, the headline lives inside its header. 2. No two-button CTA row floating free of the panel, the primary action sits inside the same card as the pulse line. 3. No decorative heartbeat glyph or icon standing in for the real pulse line, the mechanism itself is the only heartbeat graphic. ## Voice & Tone calm, plain-language, reassuring, no clinical jargon. In practice: - **Button label**: "Book a visit", not "Schedule a Telehealth Consultation" - **Error message**: "Reschedule needed. Dr. Marsh isn't available Aug 12 anymore.", not "ERROR: Provider unavailable" - **Empty state**: "No visits scheduled right now.", not "You have no upcoming appointments in the system" - **Success status**: "Confirmed for Aug 12", not "Your appointment has been successfully confirmed!" ## Accessibility Baseline - Focus ring: `2px solid var(--accent-alt)`, `2px` offset, applied via `:focus-visible` on every interactive element (links, buttons, inputs, select, textarea), never removed without a visible replacement. - Reduced motion: the pulse-line draw-in and the live-dot loop both check `prefers-reduced-motion: reduce`, the line renders fully drawn with no dot animation when it's set. - Minimum tap target: `44px` in the smallest dimension for every button and form control on touch viewports. - Color is never the only signal: visit status pairs color with a plain-language word ("Confirmed", "Awaiting you"), the reschedule state pairs coral with a full sentence and a retry action, not a bare icon. ## Components ### Navigation Single-line bar, `64px` tall, `--panel` background with a `--border` bottom hairline, sticky to the top. Left: a small "back to gallery" utility link. Center-left: the mark + wordmark lockup. Right: a Visits link plus one primary button. Collapses to just the lockup and primary button under `480px`, no hamburger, there are only two destinations. ### Buttons Three variants, one radius (`--radius-pill`), colors assigned by role, not repeated: - **Primary**: `--accent-alt` fill, white label, used once per view for the single real action ("Book a visit", "Submit and request a time"). - **Secondary**: transparent fill, `1px solid --border`, `--ink` label. Retry/reschedule actions. - **Ghost**: text-only, `--dim` label, `--ink` on hover. Tertiary actions like "watch it settle." All variants define `:hover` (brightness/border shift), `:active` (`transform: scale(0.98)`), `:disabled` (`opacity: .4`, `pointer-events: none`), and `:focus-visible` (teal outline). ### Care-team cards A flat panel per provider: an initials avatar in a teal-bordered circle, name, and role. No stock photography, initials keep the focus on "you'll see the same person," not a headshot. ### Table Header row in `--surface-raised`/`--dim` bold labels, body rows in `--panel`, one `--border` rule between rows, row hover to `--surface-raised`. Status cells are bold text in `--accent-alt` (confirmed) or `--dim` (pending), never a bare colored dot. ### Forms Label above control, `--text-small`/`--dim`/600 weight. Select and textarea share the same `--panel` background, `1px solid --border`, `--radius`, `--ink` text. On `:focus`, border becomes `--accent-alt` and the background lifts to `--surface-raised`. On error, the border becomes `--accent` and the helper text explains what to add, never "required field." ### Status / feedback states - **Loading**: a skeleton shaped like the real content (bars matching text line-heights), not a spinner. - **Empty**: a plain sentence in `--dim`, no illustration. - **Error**: an inline `--accent` message with a concrete next step, never a silent failure. - **Success / status badge**: `--radius-pill`, `1px solid --accent-alt`, `--accent-alt` text on transparent fill, not filled, filled would compete with the primary button's exclusive use of a solid `--accent-alt` fill. ### Footer One row: practice name/tagline on the left, real page links on the right (Visits, back to gallery). No invented legal or insurance text. ## Do's and Don'ts ### Do - Let the pulse line be the only element on the page allowed a full-bleed, full-width scroll-drawn moment. - Reserve `--accent` for the pulse line and states that genuinely need a patient's attention, never as decoration. - Use bold weight, not color alone, to distinguish status words in the table. - Keep the live-dot loop small and slow, it signals "active," not "urgent." - Keep form copy in the patient's own words framing ("What's going on"), never clinical intake-form phrasing. ### Don't - Don't fill a large surface (a button, a full banner) solid coral, that reads as an alert, route large fills to the calmer teal instead. - Don't add a notification badge count, a hospital-portal staple this brand deliberately avoids. - Don't replay the pulse draw-in on every scroll pass, reveal once per view. - Don't stack more than one primary button in the same view. - Don't fill a status badge solid, badges stay outlined so the primary button remains the only solid-fill element on screen. ## Known Constraints - Two routes now exist (home, visits), still no real video-visit interface or payment/insurance flow, no responsive breakpoint table beyond a single-column mobile collapse, no authenticated patient account (the "Book a visit" and intake form are not wired to anything real, this is a concept demo).