The visual system behind Evolve: The Restoration Mastermind, documented as it is actually built — every value below was read out of the shipping HTML, not specified for it. Counts show how many times each value appears across the 28 pages.
Four constraints shape every decision in this system. They are not incidental — they explain why the brand is expressed as copied CSS rather than a shared stylesheet.
Every page is a self-contained HTML file with inline <style> and <script>. Open it in a browser and it works.
Pages carry HubSpot template tags and are pasted into the CMS. The theme fights back, so overrides are defensive.
The :root block is duplicated per page. This is the system's single biggest source of drift.
All interactivity is vanilla JavaScript. Nothing is pulled from a package registry.
Every file below downloads on click. The white lockup is the master — the site is dark-ground-first, so it is the version in the nav on all 28 pages. The navy, blue and black versions are the same artwork recoloured for grounds the white one cannot sit on.
Primary lockup
Mark on its own
Banner — mark as watermark
There is no vector artwork anywhere on the site — every placement, including the nav, uses the same 268×80 raster. It holds up on screen and falls apart above roughly 270px wide. Anything printed, embroidered, or put on a banner needs vector art that does not currently exist in this library.
The favicon sits on #102B72, a blue that appears nowhere else in the
system — --blue-navy is #0D2240. It is the one place
the identity steps outside its own ramp.
The palette is a single hue ramp from near-black navy to pale sky. There is no secondary brand color — contrast and depth come from position on the ramp, never from a competing hue.
A cool grey scale carries all body copy on light grounds. It runs slightly blue, which is why it sits under the accent without muddying.
Eight pages add a naming layer on top of the raw tokens. This is the more maintainable pattern and is worth extending to the rest — it lets a page change meaning without changing hex values.
| Alias | Resolves to | Role |
|---|---|---|
| --color-accent | --blue-primary | Links, primary actions |
| --color-accent-hover | --blue-light | Hover on accent |
| --color-text-primary | --gray-800 | Body copy on light |
| --color-text-secondary | --gray-600 | Supporting copy |
| --color-text-muted | --gray-500 | Captions, meta |
| --color-bg-section | --gray-50 | Alternating section ground |
| --color-bg-dark | --blue-darker | Dark section ground |
| --color-border | --gray-200 | Dividers, card edges |
One family across the entire site. Both --font-display and
--font-body resolve to Poppins, so hierarchy is built from weight, size,
case and tracking alone — never from a second typeface. This page is set in it.
The archive holds the full family — Thin through Black, every weight with its italic — plus the license. Six of those faces carry the site: Regular, Italic, Medium, SemiBold, Bold and ExtraBold. Install at least those, because the brand builds its hierarchy from weight alone, and a missing weight shows up immediately as a synthesized, slightly-wrong face.
A single identical request sits at the top of the <style> block on
all 31 HTML files. There is no <link> tag and no preconnect anywhere
in the site.
@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,400;1,500;1,600&display=swap');
| Face | Weight | Requested | Actually used |
|---|---|---|---|
| Light | 300 | Yes | never used |
| Regular | 400 | Yes | 25 declarations |
| Medium | 500 | Yes | 91 declarations |
| SemiBold | 600 | Yes | 231 declarations |
| Bold | 700 | Yes | 87 declarations |
| ExtraBold | 800 | Yes | 66 declarations |
| Regular Italic | 400i | Yes | 20 declarations |
| Medium Italic | 500i | Yes | never used |
| SemiBold Italic | 600i | Yes | never used |
Nine faces are requested; six carry the entire site. SemiBold 600 alone accounts for roughly two in five weight declarations — it is the true workhorse of the brand.
| Declaration | Where | Uses |
|---|---|---|
| 'Poppins', sans-serif | Component-level rules | 69 |
| 'Poppins', sans-serif !important | body, body * — HubSpot guard | 29 |
Poppins is pulled with @import from inside the page's own
<style> block. The browser cannot discover the font stylesheet
until it has downloaded and started parsing the page CSS, so the two requests run
in sequence rather than in parallel. A <link> in the
<head> would start the fetch immediately.
Not one page carries
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>.
That leaves a full DNS lookup, TCP handshake and TLS negotiation on the critical
path before the first glyph can download.
The stack ends at sans-serif. With display=swap set, text
paints first in whatever generic sans the OS picks — metrically nothing like
Poppins, which is geometric with a tall x-height. The result is a visible reflow on
every first load. A metric-compatible fallback would absorb most of it.
Light 300, Medium Italic 500 and SemiBold Italic 600 are requested but appear nowhere in the CSS. Trimming them from the URL is a free reduction with no visual consequence.
The system has one governing rule, and it is consistent enough across the site to be treated as law: as type gets smaller, it gets more uppercase and more letterspaced; as it gets larger, it gets tighter and stays sentence case. Nothing in between is uppercase.
Every named text role in the system, as built. Where a role appears on both dark and light grounds, both colors are given — the site is dark-ground-first, so the dark value is the primary one.
| Role | Size | Wt | Case | Tracking | Color |
|---|---|---|---|---|---|
| Hero headline | clamp(2.5rem, 5vw, 3.5rem) | 800 | Sentence uppercase on 6 heroes | -0.02em | --white |
| Hero eyebrow | 0.9rem | 500 | Uppercase | 0.15em | --blue-light |
| Hero subtitle | 1.25rem | 400 | Sentence | normal | --white / 70% |
| Section heading | clamp(2rem, 4vw, 3rem) | 700–800 | Sentence | -0.02em | --white · --blue-dark |
| Section eyebrow | 0.85rem | 600 | Uppercase | 0.15em | --blue-primary · --blue-accent |
| Section lede | 1.1rem | 400 | Sentence | normal | --gray-600 |
| Card title | 1.35–1.4rem | 700 | Sentence | normal | --white · --blue-dark |
| Stat number | 4rem | 800 | — | normal | --white |
| Stat label | 1rem | 500 | Sentence | normal | --white / 85% |
| FAQ question | 1.05rem | 600 | Sentence | normal | --blue-dark |
| Button label | 1rem | 600 | Sentence | normal | --blue-dark on white |
| Nav CTA label | 0.9rem | 600 | Sentence | normal | --blue-dark on white |
| Nav link | 0.95rem | 500 | Sentence | normal | --white / 90% |
| Footer link | 0.95rem | 400 | Sentence | normal | --white / 80% |
| Body copy | 1rem | 400 | Sentence | normal | --gray-600 · --gray-800 |
Of 132 text-transform declarations across the site, 118 are plain
uppercase and they cluster almost entirely on labels at 0.85–0.9rem.
The remainder are deliberate exceptions.
0.1em, because Poppins caps set too tight to read at small sizes without it..hero h1 carries text-transform: uppercase !important on 6 pages. This is a deliberate treatment, not a HubSpot guard, and it is the one place large type goes uppercase.text-transform: none !important — 6 declarations exist purely to stop the HubSpot theme forcing uppercase on component headings. Never remove these.
.section-eyebrow and .section-label are the same component
with two names, and their tracking is inconsistent: 0.15em on some pages,
0.1em on others, with 0.05em, 0.08em and
0.25em also in circulation. Pick one — 0.15em is the
most common — and collapse the two class names into it.
Individual roles matter less than the combinations they form. Every block below is a pattern that repeats across the site, built with the real specifications and set in real copy taken from the live pages. Colors are literal brand values, not this document's theme — each scene shows what a visitor actually sees.
Scale with systems. Lead with confidence.
Book a Strategy SessionTactical playbooks, weekly coaching, and a peer community built around the way restoration businesses actually grow.
--blue-primary →
heading clamp(2–3rem)/700/-0.02em in --blue-dark →
lede 1.1/400/lh 1.8 in --gray-600
The same header stack, inverted. Only two things change: the label moves to --blue-accent and the heading goes white.
--blue-accent,
heading to --white, lede to white at 70%
Tactical playbooks, weekly coaching, and a peer community for owners building their first systems.
Executive-level strategy and elite peer advisory built for the complexity of the $5MM–$20MM stage.
--blue-dark → body 1rem/400/lh 1.7 in
--gray-600. No eyebrow inside cards — the section header carries it.
Thirty minutes with a coach who has run a restoration business. You leave with a written next step, whether or not you join a program.
--blue-dark →
answer 1rem/400/lh 1.7 in --gray-600. Question and answer sit only
0.05rem apart in size; weight alone separates them.
Clarity, focus, and momentum — without distraction.
Book a Strategy SessionMeasured on the real pairs above against WCAG 2.1 AA — 4.5:1 for text under 24px, 3:1 for larger or bold display type.
| Combination | Ratio | Required | Result |
|---|---|---|---|
Hero eyebrow --blue-light on navy | 3.87 | 4.5 | fails AA |
Section label --blue-accent on navy | 4.83 | 4.5 | passes |
Section eyebrow --blue-primary on white | 6.16 | 4.5 | passes |
| Hero subtitle, white 70% on navy | 8.40 | 3.0 | passes |
| Nav link, white 90% on darker | 13.95 | 4.5 | passes |
| Footer link, white 80% on darker | 11.28 | 4.5 | passes |
Body --gray-600 on white | 7.56 | 4.5 | passes |
Body --gray-600 on --off-white | 7.22 | 4.5 | passes |
Button label --blue-dark on white pill | 14.48 | 4.5 | passes |
White on --blue-primary | 6.16 | 4.5 | passes |
--blue-light #3b7dd8 at 0.9rem on --blue-navy measures
3.87:1 against a 4.5:1 requirement. The fix is already in the system: the same role
on section headers uses --blue-accent #4a90e2, which measures 4.83:1.
Switching hero eyebrows to --blue-accent resolves it with no new color
and no visible change in character.
Content sits in a 1200px container. The system has two real breakpoints —
1200px where the nav collapses, and 768px where everything
stacks. A handful of pages introduce others; treat those as exceptions, not options.
| Token | Value | Role | Uses |
|---|---|---|---|
| Container | 1200px | Standard content width | 118 |
| Wide container | 1400px | Full-bleed galleries, marquees | 20 |
| Reading column | 700–900px | Prose, forms, legal pages | 63 |
| Breakpoint · tablet | 1200px | Nav collapses to hamburger | 28 |
| Breakpoint · mobile | 768px | Grids stack to single column | 72 |
| Section rhythm | 5rem / 6rem / 8rem | Vertical padding, light to heavy | 17 |
Two shape ideas carry the whole system: fully-round pills for anything actionable, and generously rounded rectangles for anything containing content.
| Radius | Applied to | Uses |
|---|---|---|
| 50px | Buttons, badges, pills — the signature shape | 81 |
| 50% | Avatars, icon circles, play buttons | 66 |
| 16px | Large cards, feature panels | 63 |
| 12px | Standard cards, inputs, media | 63 |
| 20px | Oversized feature panels | 27 |
| 8px | Small chips, inline elements | 13 |
Shadows are pure black at low alpha, blurred wide and offset down. They read as depth against dark grounds rather than as drop shadows.
| Level | Value | Uses |
|---|---|---|
| Subtle | 0 4px 30px rgba(0,0,0,.15) | 15 |
| Card | 0 8px 25px rgba(0,0,0,.2) | 21 |
| Raised | 0 10px 40px rgba(0,0,0,.3) | 25 |
| Modal | 0 15px 40px rgba(0,0,0,.3) | 8 |
| Overlay | 0 24px 48px rgba(0,0,0,.4) | 6 |
Gradients move along the blue ramp at a consistent 135° diagonal. They never cross hues.
Image overlays use a separate pattern — linear-gradient(to top, var(--blue-navy), transparent) —
to seat photography into a dark section without a hard edge.
One shape, three treatments. Pill radius, 1rem 2rem padding, weight 600,
0.3s ease transition. On dark grounds the primary button inverts to white
with dark text — that inversion is the system's most recognizable move.
0.85rem, weight 600, uppercase, 0.1em tracking, accent blue, 1rem margin below. Precedes almost every section heading.
Fixed, rgba(--blue-darker, .95) with a 10px backdrop blur and a 1px white-10% bottom border. Logo at 40px, 30px on mobile.
Cards cloned in JS to fill the viewport, widths measured, keyframes generated at runtime. Pauses on hover and on mobile tap.
Click toggles an .open class. Height animates; the chevron rotates. No library.
Modal overlay with a circular play button at border-radius: 50%, play/pause toggle, click-outside to close.
Slides up from the bottom edge carrying the HubSpot payments iframe. Radius 24px 24px 0 0.
Motion is reserved for entrances. Content fades up as it scrolls into view, driven by
an Intersection Observer toggling a .visible class. Nothing loops except
the partners marquee.
| Property | Value |
|---|---|
| Scroll entrance | opacity 0→1, translateY(40px)→0 |
| Entrance timing | 0.8s cubic-bezier(0.16, 1, 0.3, 1) |
| Interactive transition | all 0.3s ease |
| Slow transition | all 0.4s ease |
| Marquee | infinite linear translateX, keyframes built in JS |
HubSpot's theme CSS loads after the page's own styles and will override them. These defensive rules are mandatory, not stylistic.
font-family on body, body * must carry !important, or the theme font replaces Poppins site-wide.text-transform: none !important on headings inside components — the theme forces uppercase.letter-spacing: normal !important on those same headings.templateType: page comment and carries
and
.Because tokens are copy-pasted rather than imported, pages have fallen out of sync. None of these break anything today — they are the places where the next change will cost more than it should.
--off-white is #f8fafc on 23 pages and #FDF8F3 on event-couples_2026.html — a cool grey and a warm cream sharing a name. Rename the warm one.
event-couples_2026.html also introduces --gold #C9A227, --gold-dark #A68520 and --tan #C4B5A5. If that palette is intentional for couples events, it belongs in this document as a named sub-brand. If not, it should go.
The #1a1f26 → #0d1117 gradient appears on 6 pages as raw hex with no variable behind it. It is effectively a fourth dark ground operating outside the ramp.
--blue-darker is defined on 29 pages but --blue-primary on only 25, and the semantic alias layer on just 8. Pages are not starting from the same base.
cta-btn, btn-primary, final-cta-btn, team-card-btn, resource-card-btn and ten more mostly render the same pill. One .btn base with modifiers would collapse them.
Over 25 distinct box-shadow values are in use where five levels would cover every case. Only one page defines --shadow-sm/md/lg.
Alongside 1200px and 768px, the site uses 900, 968, 1000, 700, 650, 600 and 480px. Each one is a place where layout can break without anyone noticing.