# Design Audit — Goodwalk (Codebase-wide) Re-audited beyond ValuesSection. Read `sections.css` (1130 lines), `typography.css`, `responsive.css`, `variables.css`, and surveyed each major surface (Hero, Intro, PageHeader, Footer, FAQ, Instagram, Mobile Menu, Testimonial card). Findings below cite actual files/lines. --- ## Critical issues ### 1. Three different "eyebrow" components exist, sharing no DNA **Problem.** The same UX element — small uppercase intro label above a heading — is rendered three incompatible ways: - `.eyebrow` (typography.css:100): font-body, 12px, 700, `0.09em`, green text, no background. - `.hero-kicker` (sections.css:69): font-body, 12px, 700, `0.08em`, yellow text in a *yellow pill with yellow border*. - `.intro-kicker` (sections.css:408): font-body, 12px, 600 (weight differs), `0.18em` (tracking differs — over 2× the others), white-58% text with a *yellow rule prefix*. Plus `.values-eyebrow`, `.booking-eyebrow`, `.footer-col-label`, `.intro-meta` are all variants. **Root cause.** No eyebrow primitive in the system. Each section author re-implemented from scratch. **Perception.** Reader senses different sections were designed at different times. Brand voice fragments. **Fix.** One `` component. Three variants only. Migrate all six existing implementations. **Class: System debt.** --- ### 2. Six different primary heading scales **Problem.** | Selector | Scale | Weight | Tracking | |---|---|---|---| | `.section-heading` | clamp(30, 4.6vw, 44) | 800 | -0.035em | | `.hero-text h1` | clamp(34, 4vw, 56) | 800 | -0.045em | | `.intro-headline` | clamp(30, 4.4vw, 54) | **500** | -0.02em | | `.ph-title` | clamp(34, 4vw, 56) | 800 | -0.04em | | `.info-block h2` | clamp(28, 2.4vw, 32) | 700 | -0.02em | | `#instagram h2` | clamp(30, 3vw, 36) | 700 | -0.02em | Six clamp formulas. Five tracking values. Weights ranging 500→800. The base `h2 { font-weight: 700 }` (typography.css:58) conflicts with `.section-heading { font-weight: 800 }` (typography.css:79) — actual rendered weight depends on whether the heading author remembered to apply the class. **Root cause.** No `--text-display`, `--text-h1`, `--text-h2` tokens. Every author authors a fresh clamp. **Perception.** Headings jump in size and weight as you scroll. The 500-weight `.intro-headline` reads as a different brand from the 800-weight `.hero h1` directly above it. **Fix.** Define type tokens: `--text-display` (52/800/-0.04), `--text-h1` (44/800/-0.035), `--text-h2` (32/700/-0.02), `--text-h3` (22/700/-0.02), `--text-body-lead` (17/500/0), `--text-body` (16/400/0). Map every heading. Delete the per-section clamps. **Class: Design debt + System debt.** --- ### 3. The Instagram section is a chromatic anomaly **Problem.** Section background sequence on the homepage: `#hero` (green) → `#intro` (green) → `#promise/#values` (off-white) → `#services` (white) → `#testimonials` (white) → `#info` (white) → `#newlead` (white) → `#instagram` (**solid #ffd100 full-bleed**) → `footer` (green). The Instagram block is the only place brand yellow is used as a *page section*. It's also where pure black-on-yellow body text appears at `rgba(0, 0, 0, 0.6)` (sections.css:738). **Root cause.** Brand yellow was treated as both an accent (CTAs, highlights) AND a surface (this section). Premium products pick one. **Perception.** Reads as advertising, not editorial. Breaks the calm green/off-white rhythm. The user scrolls into a marketing banner mid-page. **Fix.** Either (a) demote to a green section with yellow accents inside, or (b) shrink to a narrow card-on-off-white block. Reserve `var(--yellow)` for accents only. **Class: Design debt + Conversion debt** (it sits right before footer — possibly the last impression). --- ### 4. Hero animation system runs on a different clock than the rest of the page **Problem.** Hero entry: image 1.6s, text rise 0.85s, underline draw 0.9s @ 900ms delay, star pop 0.45s starting at 820ms. Total animation completes ~1.7s after load. Rest of page: reveal-block now 0.3s opacity / 0.45s transform. Mobile menu: 180–220ms. Button hover: 0.16–0.22s. **Root cause.** Hero was authored before the reveal/motion system existed. Never reconciled. **Perception.** The hero feels heavy and ceremonial; the rest of the page feels snappy. Two products bolted together. **Fix.** Cap all hero animations at 0.5s. Reduce delays — first text element by 80ms, each subsequent +60ms. Drop the underline-draw delay from 900ms to 350ms. Keep the elegance, lose the lethargy. **Class: Polish debt.** --- ### 5. `--space-container-x-tablet` breaks the gutter rhythm **Problem.** variables.css:145 — `--space-container-x-tablet: 30px`. Desktop value is `clamp(24px, 4vw, 48px)`. At 1024px width (the tablet breakpoint), 4vw = 41px. The tablet override drops gutters to 30px, then back up to 41–48px on desktop. A user resizing a window or rotating an iPad sees the gutters *narrow* then *widen*. **Root cause.** A patch fix applied at the tablet breakpoint that doesn't share the desktop formula. **Perception.** Visible engineering. Anyone who notices the layout shift on resize loses trust. **Fix.** Either delete `--space-container-x-tablet` and let the clamp handle the breakpoint, or make tablet = `clamp(20px, 3vw, 32px)` so the curve is continuous. **Class: System debt.** --- ### 6. Card system doesn't exist — each section invents its own **Problem.** Same UX primitive (an elevated content card), shipped four ways: | Component | Radius | Padding | Background | Shadow | |---|---|---|---|---| | `.testimonial-card` | 28px | 36px 32px | gradient panel→panel-soft | inset + shadow-md | | `.faq details` | 16px | 18px 22px | surface-page | shadow-lg on open only | | `.values-photo-card` | 28px → md mobile | n/a | beige | inset + card | | `.booking-field-card` | 24px | 24px 22px | (varies) | (varies) | Plus `.values-point` (no radius, pure surface), `.mobile-menu-links a` (16px radius), `.ph-media` (28px), `.intro-google` (pill). **Root cause.** No `` primitive. Every author re-decides. **Perception.** Adjacent sections feel like they were copy-pasted from different Behance projects. **Fix.** One Card system: `--radius-lg` (20px) for cards, `--space-7` (32px) padding, `--shadow-card` standard. Variants: `card`, `card-elevated`, `card-quiet`. Migrate all four. **Class: System debt + Design debt.** --- ### 7. Footer typography is a four-size jumble **Problem.** Within the footer alone: 14px (`.footer-brand p`, `.footer-nav a`), 13px (`.footer-bottom`, `.footer-contact-link`), 12px (`.footer-back-top`, `.footer-social-invite`), 10px (`.footer-col-label`), plus 14px (`footer h4`). Five sizes in a quiet auxiliary surface. Add opacities: `.footer-brand p` 0.72, `.footer-nav a` 0.72, `.footer-contact-link` 0.7, `.footer-bottom` 0.6, `.footer-back-top` 0.5, `.footer-col-label` 0.5. **Root cause.** Each footer column was sized independently to hit visual targets, not to scale. **Perception.** The footer feels busy in a quiet way — many small elements clamoring for slightly different attention. **Fix.** Two sizes: 14px (links/copy) + 12px (label/legal). Two opacities: 0.85 (active text) + 0.55 (labels). Delete the rest. **Class: Polish debt + UX debt.** --- ### 8. `.section-heading` color is pure `#000`, used on warm off-white backgrounds **Problem.** typography.css:82 — `.section-heading { color: var(--text-strong); }` where `--text-strong: #000`. Promise, Values, Testimonials, Services, Info all render their primary heading in pure black on a warm `--off-white #f8f7f2`. Same pattern as the `.btn-yellow` issue I already fixed: pure black against a warm surface reads as harsh and clinical. **Root cause.** `--text-strong` is a "darkest-possible" token used reflexively for headings instead of a heading-specific token. **Perception.** Headings shout. The rest of the type — body in `var(--text)` (#2e3031), muted in green-cast grays — is warm. Headings are not. **Fix.** `.section-heading { color: var(--text-heading); }` (which is `#1f2421`, near-black with a green cast). Or `var(--gw-green)` for full editorial treatment. The dark-green-on-warm-cream pairing is the brand's most premium register. **Class: Design debt.** --- ### 9. Two Google trust-mark components live as duplicates **Problem.** `.hero-trust-mark` (sections.css:153) and `.intro-google-mark` (sections.css:491). Same Google "G" circle, same white background, same shadow recipe with slightly different parameters (`0 2px 8px rgba(0,0,0,0.25)` vs `0 4px 12px rgba(0,0,0,0.25)`). Different sizes (28px vs 36px). Different parent chip layouts (`.hero-trust-chip` vs `.intro-google`). **Root cause.** Copy-paste authoring. The second one was built without abstracting the first. **Perception.** A user who scrolls hero → intro sees the same trust signal twice in slightly different sizes. Either feels redundant or sloppy depending on attention level. **Fix.** One `` component. Two clean sizes (24, 36). Single shadow token. **Class: System debt + Conversion debt** (trust signals matter; redundant ones dilute). --- ### 10. Letter-spacing on headings has four values **Problem.** `-0.02em` (h2/h3, intro-headline, info-block h2), `-0.035em` (section-heading), `-0.04em` (ph-title), `-0.045em` (hero h1). Four bespoke tracking values. **Root cause.** Each heading was tuned visually without a tracking scale. **Perception.** Headings at similar sizes (`.intro-headline` 54px at `-0.02em` vs `.hero h1` 56px at `-0.045em`) feel like they're set in *different fonts*, when they're actually both Unbounded. **Fix.** Two tracking values: `-0.035em` for display (36px+), `-0.02em` for everything else. Lock in the type tokens. **Class: Design debt.** --- ### 11. Hero text relies on hand-numbered nth-child animation delays **Problem.** sections.css:272–278 — `.hero-text > :nth-child(1) { animation-delay: 160ms; }` through `:nth-child(7)`. Seven hardcoded children. If the hero content shape changes (e.g., remove the kicker, add a phone CTA), the choreography breaks silently. **Root cause.** Animation was authored against current markup, not against generic children. **Perception.** Probably invisible to most users *until* it breaks — at which point an element pops in without animation while others slide. **Fix.** Use CSS counter or a Svelte action that applies `--reveal-delay` per child. Or accept staggered delays via `:nth-child(n)` formula: `animation-delay: calc(160ms + (var(--i, 0)) * 100ms)`. **Class: System debt + Polish debt.** --- ### 12. Mobile hero CTA shrinks to 12px font, 9px horizontal padding under 480px **Problem.** responsive.css:767–774 — primary `.btn-yellow` becomes `padding: 13px 9px; font-size: 12px; line-height: 1.1` on iPhone SE-class widths. This is the *primary booking CTA* — the conversion target. **Root cause.** Two CTAs side-by-side in a row at very narrow widths. Author chose to squeeze both rather than stack. **Perception.** On a 320px screen, the booking button reads as cramped, low-confidence. CTA hierarchy collapses. **Fix.** Keep buttons stacked under 480px (already done at 768px — extend the rule). Primary CTA: full-width, 14px font, 14px vertical padding. Secondary link below. **Class: Conversion debt.** --- ### 13. Hardcoded shadows everywhere despite a complete shadow token set **Problem.** Beyond ValuesSection, the same pattern repeats: - `.testimonial-card:hover` — `0 8px 40px rgba(0, 0, 0, 0.08)` (sections.css:620). Not a token. - `.hero-trust-mark` — `0 2px 8px rgba(0, 0, 0, 0.25)` (line 161). Not a token. - `.intro-google-mark` — `0 4px 12px rgba(0, 0, 0, 0.25)` (line 499). Not a token. - `.ph-media` — `0 16px 40px rgba(var(--ink-rgb), 0.08)` (line 1027). Not a token. - `.intro-google` — uses inset shadow tuples directly (lines 470, 482). **Root cause.** Authors didn't know which token to grab from 18 shadow options. **Perception.** Elevations feel inconsistent — some cards lift more, some less, even when visually they're the same depth. **Fix.** Five tokens, not eighteen: `--shadow-flat`, `--shadow-card`, `--shadow-elevated`, `--shadow-floating`, `--shadow-modal`. Migrate everything. **Class: System debt.** --- ### 14. The `.intro-kicker` heading-overline pattern is design-y; nothing else on the page uses it **Problem.** `.intro-kicker` (sections.css:408) uses a horizontal rule + uppercase text pattern (line + word). It's stylish. But it appears nowhere else. Adjacent sections use pill eyebrows, naked uppercase, or nothing. **Root cause.** Section author drew inspiration that didn't propagate. **Perception.** The intro feels visually distinct in a way that disconnects it from the rest of the page — over-designed compared to its neighbors. **Fix.** Pick one: either commit and use the rule pattern in 2-3 more sections (rhythm); or drop it and use the standard `.eyebrow`. **Class: Design debt.** --- ### 15. FAQ details radius (16px) doesn't match testimonial card (28px), values bento (20px), or photo card (28px) — they all sit on the same page **Problem.** Cumulative effect of issue #6. The Info section has FAQ cards next to other cards with completely different silhouettes. **Perception.** Visual rhythm dissolves. **Fix.** All cards at `--radius-lg` (20px). Period. **Class: Polish debt.** --- ### 16. Footer container padding doesn't share the global gutter system **Problem.** sections.css:600 — `footer { padding: 60px 50px 32px; }`. The 50px is hardcoded. The mobile override at responsive.css:678 uses `var(--space-container-x-mobile)` — correct. But desktop is a literal. **Same problem** at `#instagram { padding: 60px 50px; }` (sections.css:732) and `.ph-inner { padding: 0 50px; }` (line 949). **Perception.** Footer/instagram/page-header gutters jitter against section gutters by a few pixels — the same issue I documented for ValuesSection, repeated across the codebase. **Fix.** Replace every `50px` desktop gutter with `var(--space-container-x)`. **Class: System debt.** --- ### 17. Section vertical rhythm collapses on mobile to a single value **Problem.** variables.css:172–178 — on mobile, all four section padding tiers (`--space-section-featured-y`, `--space-section-support-y`, `--space-section-form-y`, `--space-section-page-y`) collapse to `--space-section-mobile-y` (40px). On desktop, featured sections breathe more than supporting. On mobile, every section has identical vertical padding. **Root cause.** Pragmatic — mobile space is scarce. But the *intent* (featured sections feel more important) disappears. **Perception.** Mobile users get a "flat" page where every section has equal visual weight. Hero → Intro → Promise → Services all feel like peers. **Fix.** Two mobile tiers: `--space-section-featured-y-mobile: 56px`, `--space-section-mobile-y: 40px`. Featured sections get 16px more breathing room. Still respects mobile constraints. **Class: Design debt.** --- ### 18. `#hero::after` gradient mask has a 4-stop gradient with literal RGB **Problem.** sections.css:32–45 — a four-stop gradient `transparent 18% → 26% brand → 78% brand → solid brand 86%`. Plus a mobile-only override at lines 293–303 with *different* stops (`22% → 45% → 88% → 78%`). The math doesn't read as deliberate; it reads as tuned by trial and error. **Root cause.** Hero image overlay was fine-tuned to one image asset. Different images won't behave the same. **Perception.** Probably invisible. But if the hero photo ever changes (winter scene, different dog), the overlay won't land right. **Fix.** Reduce to 2 stops: `transparent 30% → solid brand 95%`. Test against multiple photos. **Class: Polish debt.** --- ### 19. Hero kicker mobile color is `rgba(white, 0.48)` on dark green **Problem.** responsive.css:358 — `color: rgba(var(--white-rgb), 0.48)` for the kicker on mobile. At 10px uppercase 0.13em on a dark green-tinted gradient, that's somewhere around 4–5:1 contrast against the gradient. Borderline AA. **Root cause.** Designer wanted the kicker quiet against the loud headline. But mobile users get a small device + outdoor sunlight + decreased contrast. **Perception.** Some users won't read the kicker. Those who do will strain. **Fix.** `rgba(white, 0.72)` at 11px on mobile. Still quiet, properly accessible. **Class: UX debt.** --- ### 20. Three styling paradigms cohabit **Problem.** The codebase uses: 1. **Global utility classes** (`.btn`, `.eyebrow`, `.section-heading`, `.testimonial-card`, `.faq`) defined in `src/lib/styles/*.css`. 2. **Component-scoped styles** inside Svelte `