Construction — Services
A rugged construction company services page with a hi-vis hero, filterable service chips, a grid of trade cards for new builds remodels additions concrete roofing electrical and plumbing, a four step process timeline, and a get an estimate call to action band.
MCP
Code
:root {
--hv: #f5c518;
--steel: #2b3440;
--steel-d: #1b222b;
--bg: #eceae6;
--ink: #1a1d22;
--muted: #666c75;
--orange: #e8642a;
--white: #ffffff;
--line: #d2cfc8;
--radius: 6px;
--shadow: 0 2px 0 rgba(27, 34, 43, 0.08), 0 10px 24px rgba(27, 34, 43, 0.08);
--font: "Inter", system-ui, -apple-system, sans-serif;
}
* {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
font-family: var(--font);
color: var(--ink);
background: var(--bg);
line-height: 1.55;
-webkit-font-smoothing: antialiased;
}
h1,
h2,
h3,
p,
dl,
dd,
ul,
ol {
margin: 0;
}
ul,
ol {
list-style: none;
padding: 0;
}
a {
color: inherit;
text-decoration: none;
}
.wrap {
width: min(1080px, 92vw);
margin-inline: auto;
}
:focus-visible {
outline: 3px solid var(--orange);
outline-offset: 2px;
}
/* ---------- header ---------- */
.site-head {
position: sticky;
top: 0;
z-index: 20;
background: var(--steel-d);
border-bottom: 3px solid var(--hv);
}
.head-inner {
display: flex;
align-items: center;
justify-content: space-between;
height: 64px;
}
.brand {
display: flex;
align-items: center;
gap: 10px;
color: var(--white);
}
.brand-mark {
color: var(--hv);
font-size: 1.3rem;
line-height: 1;
}
.brand-name {
font-weight: 900;
letter-spacing: 0.06em;
font-size: 1.05rem;
display: flex;
flex-direction: column;
}
.brand-sub {
font-size: 0.6rem;
font-weight: 700;
letter-spacing: 0.32em;
color: var(--hv);
}
.head-nav {
display: flex;
align-items: center;
gap: 22px;
}
.head-nav a {
color: #cfd4da;
font-weight: 600;
font-size: 0.92rem;
}
.head-nav a:hover {
color: var(--white);
}
.head-cta {
background: var(--hv);
color: var(--steel-d) !important;
padding: 8px 14px;
border-radius: var(--radius);
font-weight: 800;
}
/* ---------- buttons ---------- */
.btn {
display: inline-block;
border: none;
cursor: pointer;
font-family: inherit;
font-weight: 800;
font-size: 0.95rem;
letter-spacing: 0.01em;
padding: 13px 22px;
border-radius: var(--radius);
transition: transform 0.08s ease, box-shadow 0.15s ease, background 0.15s ease;
}
.btn:active {
transform: translateY(1px);
}
.btn-hv {
background: var(--hv);
color: var(--steel-d);
box-shadow: 0 4px 0 #c79e0d;
}
.btn-hv:hover {
background: #ffd633;
}
.btn-ghost {
background: transparent;
color: var(--white);
border: 2px solid rgba(255, 255, 255, 0.35);
}
.btn-ghost:hover {
border-color: var(--white);
}
.btn-block {
width: 100%;
}
/* ---------- hazard strip ---------- */
.hazard {
height: 12px;
background: repeating-linear-gradient(
-45deg,
var(--hv) 0 22px,
var(--steel-d) 22px 44px
);
}
.hazard-top {
margin-bottom: 0;
}
/* ---------- hero ---------- */
.hero {
background: var(--steel);
color: var(--white);
position: relative;
}
.hero-inner {
padding: 58px 0 64px;
}
.eyebrow {
text-transform: uppercase;
letter-spacing: 0.22em;
font-size: 0.72rem;
font-weight: 700;
color: var(--hv);
margin-bottom: 14px;
}
.hero h1 {
font-size: clamp(2.6rem, 8vw, 4.6rem);
font-weight: 900;
line-height: 0.96;
letter-spacing: -0.02em;
text-transform: uppercase;
}
.hero h1 .hi {
color: var(--hv);
}
.hero-lead {
max-width: 52ch;
margin-top: 18px;
color: #c8cdd3;
font-size: 1.05rem;
}
.hero-actions {
display: flex;
flex-wrap: wrap;
gap: 14px;
margin-top: 26px;
}
.hero-stats {
display: flex;
flex-wrap: wrap;
gap: 38px;
margin-top: 40px;
padding-top: 26px;
border-top: 1px solid rgba(255, 255, 255, 0.14);
}
.hero-stats dt {
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 0.12em;
color: #9aa1aa;
}
.hero-stats dd {
font-size: 1.7rem;
font-weight: 900;
margin-top: 2px;
}
/* ---------- section heads ---------- */
.sec-head {
margin-bottom: 22px;
}
.sec-head h2 {
font-size: clamp(1.7rem, 4vw, 2.4rem);
font-weight: 900;
text-transform: uppercase;
letter-spacing: -0.01em;
}
.sec-sub {
color: var(--muted);
font-weight: 600;
margin-top: 4px;
}
/* ---------- services ---------- */
.services {
padding: 56px 0;
}
.chips {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 16px;
}
.chip {
cursor: pointer;
font-family: inherit;
font-weight: 700;
font-size: 0.88rem;
padding: 8px 16px;
border-radius: 100px;
border: 2px solid var(--steel);
background: transparent;
color: var(--steel);
transition: background 0.12s ease, color 0.12s ease;
}
.chip:hover {
background: rgba(43, 52, 64, 0.08);
}
.chip.is-active {
background: var(--steel);
color: var(--white);
}
.result-count {
color: var(--muted);
font-size: 0.85rem;
font-weight: 600;
margin-bottom: 18px;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 16px;
}
.card {
background: var(--white);
border: 1px solid var(--line);
border-left: 5px solid var(--orange);
border-radius: var(--radius);
padding: 22px 20px 20px;
box-shadow: var(--shadow);
transition: transform 0.12s ease, box-shadow 0.15s ease;
}
.card:hover {
transform: translateY(-3px);
box-shadow: 0 4px 0 rgba(27, 34, 43, 0.1), 0 16px 30px rgba(27, 34, 43, 0.12);
}
.card-ico {
font-size: 1.9rem;
display: block;
margin-bottom: 8px;
}
.card h3 {
font-size: 1.18rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.01em;
}
.card p {
color: var(--muted);
font-size: 0.92rem;
margin-top: 6px;
}
.lead {
display: inline-block;
margin-top: 14px;
font-size: 0.72rem;
font-weight: 800;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(--steel-d);
background: var(--hv);
padding: 4px 10px;
border-radius: 4px;
}
.empty {
text-align: center;
color: var(--muted);
font-weight: 600;
padding: 30px 0;
}
/* ---------- process ---------- */
.process {
padding: 56px 0;
background: var(--steel-d);
color: var(--white);
}
.process .sec-sub {
color: #9aa1aa;
}
.steps {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
}
.step {
position: relative;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.1);
border-top: 4px solid var(--hv);
border-radius: var(--radius);
padding: 20px;
}
.step-no {
font-size: 2rem;
font-weight: 900;
color: var(--hv);
line-height: 1;
}
.step h3 {
margin-top: 8px;
font-size: 1.1rem;
font-weight: 800;
text-transform: uppercase;
}
.step p {
margin-top: 6px;
color: #c8cdd3;
font-size: 0.9rem;
}
/* ---------- cta ---------- */
.cta {
background: var(--orange);
color: var(--white);
}
.cta .hazard-top {
height: 12px;
}
.cta-inner {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 36px;
align-items: center;
padding: 48px 0 54px;
}
.cta-copy h2 {
font-size: clamp(1.8rem, 4vw, 2.6rem);
font-weight: 900;
text-transform: uppercase;
line-height: 1;
}
.cta-copy p {
margin-top: 12px;
max-width: 42ch;
font-weight: 500;
}
.cta-form {
background: var(--white);
border-radius: 8px;
padding: 22px;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px;
box-shadow: 0 14px 30px rgba(0, 0, 0, 0.18);
}
.field {
display: flex;
flex-direction: column;
gap: 5px;
}
.field-wide {
grid-column: 1 / -1;
}
.cta-form label {
font-size: 0.78rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--steel);
}
.cta-form input,
.cta-form select {
font-family: inherit;
font-size: 0.95rem;
padding: 11px 12px;
border: 2px solid var(--line);
border-radius: var(--radius);
background: var(--bg);
color: var(--ink);
}
.cta-form input:focus,
.cta-form select:focus {
border-color: var(--steel);
outline: none;
background: var(--white);
}
.cta-form input[aria-invalid="true"] {
border-color: #c8351a;
background: #fdecea;
}
.cta-form .btn-block {
grid-column: 1 / -1;
}
.form-note {
grid-column: 1 / -1;
margin: 0;
font-weight: 700;
font-size: 0.88rem;
color: var(--steel-d);
min-height: 1.2em;
}
.form-note.ok {
color: #1a7a3a;
}
.form-note.err {
color: #c8351a;
}
/* ---------- footer ---------- */
.site-foot {
background: var(--steel-d);
color: #9aa1aa;
padding: 22px 0;
border-top: 3px solid var(--hv);
}
.site-foot p {
font-size: 0.82rem;
}
/* ---------- responsive ---------- */
@media (max-width: 760px) {
.cta-inner {
grid-template-columns: 1fr;
gap: 24px;
}
}
@media (max-width: 520px) {
.head-nav {
gap: 14px;
}
.head-nav a:not(.head-cta) {
display: none;
}
.hero-inner {
padding: 40px 0 46px;
}
.hero-stats {
gap: 22px;
}
.hero-actions .btn {
flex: 1;
text-align: center;
}
.cta-form {
grid-template-columns: 1fr;
}
.grid {
grid-template-columns: 1fr;
}
}(function () {
"use strict";
/* ---------- service filtering ---------- */
var chips = Array.prototype.slice.call(document.querySelectorAll(".chip"));
var cards = Array.prototype.slice.call(document.querySelectorAll(".card"));
var countEl = document.getElementById("count");
var emptyEl = document.getElementById("empty");
function applyFilter(filter) {
var shown = 0;
cards.forEach(function (card) {
var match = filter === "all" || card.getAttribute("data-cat") === filter;
card.hidden = !match;
if (match) shown++;
});
if (countEl) countEl.textContent = String(shown);
if (emptyEl) emptyEl.hidden = shown !== 0;
}
chips.forEach(function (chip) {
chip.addEventListener("click", function () {
chips.forEach(function (c) {
c.classList.remove("is-active");
c.setAttribute("aria-pressed", "false");
});
chip.classList.add("is-active");
chip.setAttribute("aria-pressed", "true");
applyFilter(chip.getAttribute("data-filter"));
});
});
/* ---------- estimate form ---------- */
var form = document.getElementById("estimate-form");
var note = document.getElementById("form-note");
if (form) {
form.addEventListener("submit", function (e) {
e.preventDefault();
var name = form.elements.name;
var phone = form.elements.phone;
var service = form.elements.service;
var ok = true;
[name, phone].forEach(function (input) {
var valid = input.value.trim().length > 1;
input.setAttribute("aria-invalid", valid ? "false" : "true");
if (!valid) ok = false;
});
if (!ok) {
note.textContent = "Please add your name and a phone number.";
note.className = "form-note err";
return;
}
note.textContent =
"Thanks, " +
name.value.trim().split(" ")[0] +
" — we'll call about your " +
service.value.toLowerCase() +
" job within 48 hours.";
note.className = "form-note ok";
form.reset();
name.setAttribute("aria-invalid", "false");
phone.setAttribute("aria-invalid", "false");
});
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Ironvale Builders — Services</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<header class="site-head">
<div class="wrap head-inner">
<a class="brand" href="#top" aria-label="Ironvale Builders home">
<span class="brand-mark" aria-hidden="true">▰</span>
<span class="brand-name">IRONVALE<span class="brand-sub">BUILDERS</span></span>
</a>
<nav class="head-nav" aria-label="Primary">
<a href="#services">Services</a>
<a href="#process">Process</a>
<a href="#estimate" class="head-cta">Get an estimate</a>
</nav>
</div>
</header>
<main id="top">
<!-- HERO -->
<section class="hero" aria-labelledby="hero-title">
<div class="hazard" aria-hidden="true"></div>
<div class="wrap hero-inner">
<p class="eyebrow">Licensed & insured · Est. 2004</p>
<h1 id="hero-title">
We build it<br /><span class="hi">solid.</span>
</h1>
<p class="hero-lead">
Full-service general contracting across the Cascade valley — from ground-up new builds to
kitchen remodels, concrete flatwork and trade callouts. One crew, one warranty.
</p>
<div class="hero-actions">
<a href="#estimate" class="btn btn-hv">Get a free estimate</a>
<a href="#services" class="btn btn-ghost">Browse services</a>
</div>
<dl class="hero-stats">
<div><dt>Projects delivered</dt><dd>1,240+</dd></div>
<div><dt>Crew on staff</dt><dd>38</dd></div>
<div><dt>Workmanship warranty</dt><dd>10 yr</dd></div>
</dl>
</div>
</section>
<!-- SERVICES -->
<section id="services" class="services" aria-labelledby="services-title">
<div class="wrap">
<div class="sec-head">
<h2 id="services-title">What we do</h2>
<p class="sec-sub">Filter by the trade you need.</p>
</div>
<div class="chips" role="group" aria-label="Filter services by trade">
<button class="chip is-active" type="button" data-filter="all" aria-pressed="true">All</button>
<button class="chip" type="button" data-filter="build" aria-pressed="false">New build</button>
<button class="chip" type="button" data-filter="remodel" aria-pressed="false">Remodel</button>
<button class="chip" type="button" data-filter="additions" aria-pressed="false">Additions</button>
<button class="chip" type="button" data-filter="concrete" aria-pressed="false">Concrete</button>
<button class="chip" type="button" data-filter="roofing" aria-pressed="false">Roofing</button>
<button class="chip" type="button" data-filter="electrical" aria-pressed="false">Electrical</button>
<button class="chip" type="button" data-filter="plumbing" aria-pressed="false">Plumbing</button>
</div>
<p class="result-count" aria-live="polite"><span id="count">7</span> services</p>
<ul class="grid" id="grid">
<li class="card" data-cat="build">
<span class="card-ico" aria-hidden="true">🏗️</span>
<h3>New Build</h3>
<p>Custom homes and light-commercial from foundation to final walkthrough, managed by a single project lead.</p>
<span class="lead">Lead time · 6–14 mo</span>
</li>
<li class="card" data-cat="remodel">
<span class="card-ico" aria-hidden="true">🛠️</span>
<h3>Remodel</h3>
<p>Kitchens, baths and whole-floor refits. Dust-controlled, phased so you can stay in the home.</p>
<span class="lead">Lead time · 3–10 wk</span>
</li>
<li class="card" data-cat="additions">
<span class="card-ico" aria-hidden="true">📐</span>
<h3>Additions</h3>
<p>Second storeys, garages and in-law suites tied cleanly into your existing structure.</p>
<span class="lead">Lead time · 8–16 wk</span>
</li>
<li class="card" data-cat="concrete">
<span class="card-ico" aria-hidden="true">🧱</span>
<h3>Concrete</h3>
<p>Footings, slabs, driveways and stamped flatwork — poured level, cured right, sealed.</p>
<span class="lead">Lead time · 1–3 wk</span>
</li>
<li class="card" data-cat="roofing">
<span class="card-ico" aria-hidden="true">🏠</span>
<h3>Roofing</h3>
<p>Asphalt, metal and flat-membrane re-roofs with full tear-off and ice-and-water barrier.</p>
<span class="lead">Lead time · 2–5 day</span>
</li>
<li class="card" data-cat="electrical">
<span class="card-ico" aria-hidden="true">⚡</span>
<h3>Electrical</h3>
<p>Panel upgrades, rewires, EV chargers and code corrections by licensed journeymen.</p>
<span class="lead">Lead time · 1–4 day</span>
</li>
<li class="card" data-cat="plumbing">
<span class="card-ico" aria-hidden="true">🔧</span>
<h3>Plumbing</h3>
<p>Repipes, water-heater swaps, fixture installs and leak diagnostics with same-week callouts.</p>
<span class="lead">Lead time · 1–3 day</span>
</li>
</ul>
<p class="empty" id="empty" hidden>No services match that filter.</p>
</div>
</section>
<!-- PROCESS -->
<section id="process" class="process" aria-labelledby="process-title">
<div class="wrap">
<div class="sec-head">
<h2 id="process-title">How a job runs</h2>
<p class="sec-sub">No surprises — four clear stages.</p>
</div>
<ol class="steps">
<li class="step">
<span class="step-no">01</span>
<h3>Consult</h3>
<p>We visit the site, walk the scope and talk budget. No charge, no pressure.</p>
</li>
<li class="step">
<span class="step-no">02</span>
<h3>Estimate</h3>
<p>Itemised fixed-price quote with materials, timeline and a payment schedule.</p>
</li>
<li class="step">
<span class="step-no">03</span>
<h3>Build</h3>
<p>Permits pulled, crew scheduled, weekly progress photos straight to your phone.</p>
</li>
<li class="step">
<span class="step-no">04</span>
<h3>Handover</h3>
<p>Final inspection, punch-list cleared, and your 10-year workmanship warranty.</p>
</li>
</ol>
</div>
</section>
<!-- CTA -->
<section id="estimate" class="cta" aria-labelledby="cta-title">
<div class="hazard hazard-top" aria-hidden="true"></div>
<div class="wrap cta-inner">
<div class="cta-copy">
<h2 id="cta-title">Ready to break ground?</h2>
<p>Tell us about the project and we'll come out for a free, no-obligation estimate within 48 hours.</p>
</div>
<form class="cta-form" id="estimate-form" novalidate>
<div class="field">
<label for="ef-name">Name</label>
<input id="ef-name" name="name" type="text" autocomplete="name" required />
</div>
<div class="field">
<label for="ef-phone">Phone</label>
<input id="ef-phone" name="phone" type="tel" autocomplete="tel" required />
</div>
<div class="field field-wide">
<label for="ef-service">Service</label>
<select id="ef-service" name="service">
<option value="New build">New build</option>
<option value="Remodel">Remodel</option>
<option value="Additions">Additions</option>
<option value="Concrete">Concrete</option>
<option value="Roofing">Roofing</option>
<option value="Electrical">Electrical</option>
<option value="Plumbing">Plumbing</option>
</select>
</div>
<button class="btn btn-hv btn-block" type="submit">Request estimate</button>
<p class="form-note" id="form-note" role="status" aria-live="polite"></p>
</form>
</div>
</section>
</main>
<footer class="site-foot">
<div class="wrap">
<p>© 2026 Ironvale Builders · Lic. #CCB-204418 · Cascade Valley, OR</p>
</div>
</footer>
<script src="script.js"></script>
</body>
</html>Construction Services
A sturdy, trades-flavoured marketing page for a general contractor. The hero pairs a chunky condensed headline with a hi-vis yellow estimate button and a diagonal hazard accent strip. Below it, a row of filter chips lets visitors narrow the service grid by trade — New Build, Remodel, Additions, Concrete, Roofing, Electrical and Plumbing — each card carrying an icon, a short blurb and a typical lead-time badge.
A four-step process timeline walks clients through Consult → Estimate → Build → Handover, and a final CTA band invites them to request a free estimate with an inline form that validates and confirms without leaving the page. Filtering, the live result count and the form are all handled in vanilla JS.
The steel-and-concrete palette, bold borders and hazard diagonals give it the worksite feel without sacrificing accessible headings, labels and focus styles.
Illustrative UI only — not for real construction or contracting use.