DIY — Home Improvement Landing
A bright, practical landing page for a fictional home-improvement guide site, built with plain HTML, CSS and vanilla JavaScript. It pairs a draggable, keyboard-accessible before/after room comparison with a live materials calculator that sizes paint, tile or deck orders, adds a waste factor, and prints a monospaced cost breakdown. Rounded out by project category tiles, guide cards with skill and permit badges, a safety checklist with a completion meter, a DIY-versus-pro table and a ZIP-code pro finder.
MCP
Code
:root{
--navy:#1b2b3a; --navy-d:#132131; --navy-l:#2c4258;
--orange:#f26522; --orange-d:#d5501a; --orange-soft:#ffe9de;
--green:#3f9e5c; --green-soft:#e2f4e7;
--yellow:#f5c518; --yellow-soft:#fff5d1;
--danger:#d4503e;
--ink:#1b2b3a; --ink-2:#3f5266; --muted:#6d7d8c;
--bg:#ffffff; --surface:#f2f0ed; --surface-2:#e9e6e1; --white:#fff;
--line:rgba(27,43,58,.12); --line-2:rgba(27,43,58,.22);
--r-sm:6px; --r-md:12px; --r-lg:18px;
--sh-1:0 1px 2px rgba(27,43,58,.06), 0 2px 8px rgba(27,43,58,.05);
--sh-2:0 8px 24px rgba(27,43,58,.10), 0 2px 6px rgba(27,43,58,.06);
--sh-3:0 18px 48px rgba(27,43,58,.16);
--mono:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
}
*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
margin:0;background:var(--bg);color:var(--ink);
font-family:Inter,system-ui,-apple-system,Segoe UI,sans-serif;
line-height:1.5;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
}
h1,h2,h3{margin:0;line-height:1.12;letter-spacing:-.02em;font-weight:800}
p{margin:0}
img,svg{max-width:100%}
a{color:inherit}
.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.skip{position:absolute;left:-999px;top:8px;z-index:99;background:var(--navy);color:#fff;padding:10px 16px;border-radius:var(--r-sm)}
.skip:focus{left:8px}
:focus-visible{outline:3px solid var(--orange);outline-offset:2px;border-radius:4px}
.wrap{width:min(1160px,100% - 40px);margin-inline:auto}
.mono{font-family:var(--mono)}
/* ---------- buttons ---------- */
.btn{
display:inline-flex;align-items:center;justify-content:center;gap:8px;
font-weight:700;font-size:15px;padding:13px 22px;border-radius:999px;
border:1px solid transparent;cursor:pointer;text-decoration:none;
transition:transform .16s ease, box-shadow .16s ease, background .16s ease;
}
.btn-sm{padding:9px 16px;font-size:14px}
.btn-primary{background:var(--orange);color:#fff;box-shadow:0 6px 16px rgba(242,101,34,.32)}
.btn-primary:hover{background:var(--orange-d);transform:translateY(-2px);box-shadow:0 10px 22px rgba(242,101,34,.38)}
.btn-primary:active{transform:translateY(0)}
.btn-ghost{background:transparent;color:var(--navy);border-color:var(--line-2)}
.btn-ghost:hover{background:var(--surface);transform:translateY(-2px)}
/* ---------- nav ---------- */
.nav{position:sticky;top:0;z-index:40;background:rgba(255,255,255,.88);backdrop-filter:blur(12px);border-bottom:1px solid var(--line)}
.nav-in{display:flex;align-items:center;gap:24px;height:66px}
.brand{display:flex;align-items:center;gap:10px;text-decoration:none;font-weight:800;letter-spacing:-.02em;font-size:18px}
.brand-mark{display:grid;place-items:center;width:34px;height:34px;border-radius:10px;background:var(--orange);color:#fff}
.brand-txt em{font-style:normal;color:var(--orange)}
.nav-links{display:flex;gap:22px;margin-left:auto;font-size:14.5px;font-weight:600;color:var(--ink-2)}
.nav-links a{text-decoration:none;padding:4px 0;border-bottom:2px solid transparent;transition:color .15s,border-color .15s}
.nav-links a:hover{color:var(--orange);border-color:var(--orange)}
/* ---------- hero ---------- */
.hero{padding:64px 0 72px;background:
radial-gradient(900px 420px at 88% -10%, var(--yellow-soft), transparent 62%),
radial-gradient(700px 420px at -5% 10%, var(--orange-soft), transparent 60%),
var(--bg);}
.hero-in{display:grid;grid-template-columns:1fr 1.08fr;gap:52px;align-items:center}
.eyebrow{display:inline-flex;align-items:center;gap:8px;font-size:12.5px;font-weight:700;text-transform:uppercase;letter-spacing:.09em;color:var(--ink-2);background:var(--white);border:1px solid var(--line);padding:7px 14px;border-radius:999px;box-shadow:var(--sh-1)}
.eyebrow .dot{width:8px;height:8px;border-radius:50%;background:var(--green)}
h1{font-size:clamp(34px,4.6vw,56px);margin:18px 0 14px}
.lede{font-size:17.5px;color:var(--ink-2);max-width:47ch}
.hero-cta{display:flex;gap:12px;flex-wrap:wrap;margin:26px 0 30px}
.hero-stats{list-style:none;display:flex;gap:28px;padding:22px 0 0;margin:0;border-top:1px solid var(--line);flex-wrap:wrap}
.hero-stats strong{display:block;font-family:var(--mono);font-size:23px;font-weight:700;color:var(--navy)}
.hero-stats span{font-size:12.5px;color:var(--muted)}
/* ---------- before / after ---------- */
.ba{margin:0}
.ba-frame{position:relative;aspect-ratio:4/3;border-radius:var(--r-lg);overflow:hidden;box-shadow:var(--sh-3);border:1px solid var(--line);touch-action:none;user-select:none;background:#dfe6ea}
.scene{position:absolute;inset:0}
.scene-before{clip-path:inset(0 48% 0 0)}
.s-wall{position:absolute;inset:0 0 32% 0;background:linear-gradient(180deg,#f7f4ef,#eae4da)}
.s-wall-old{background:linear-gradient(180deg,#ded5c4,#c9bda6)}
.s-floor{position:absolute;inset:68% 0 0 0}
.s-floor-new{background:
repeating-linear-gradient(90deg,rgba(0,0,0,.07) 0 1px,transparent 1px 46px),
linear-gradient(180deg,#c98f56,#a86f3c);}
.s-floor-old{background:
repeating-linear-gradient(90deg,rgba(0,0,0,.12) 0 2px,transparent 2px 30px),
linear-gradient(180deg,#8c8577,#6d685d);}
.s-window{position:absolute;left:9%;top:14%;width:30%;height:38%;background:linear-gradient(160deg,#cfe6f2,#9dc9e0);border:6px solid #fff;border-radius:6px;box-shadow:0 8px 20px rgba(27,43,58,.16);display:grid;grid-template-columns:1fr 1fr}
.s-window i{border-right:3px solid #fff}
.s-window i:last-child{border:0}
.s-window-old{background:linear-gradient(160deg,#b9bfbb,#8d9a9c);border-color:#cfc6b4;box-shadow:none}
.s-art{position:absolute;border-radius:4px;box-shadow:var(--sh-1)}
.a1{right:16%;top:16%;width:16%;height:20%;background:linear-gradient(140deg,var(--orange),var(--yellow));border:5px solid #fff}
.a2{right:34%;top:26%;width:11%;height:13%;background:linear-gradient(140deg,var(--navy-l),#7ba8c7);border:5px solid #fff}
.s-patch{position:absolute;background:rgba(120,105,80,.5);border-radius:40% 60% 55% 45%}
.p1{right:20%;top:20%;width:13%;height:12%}
.p2{right:40%;top:34%;width:8%;height:7%}
.s-crack{position:absolute;left:46%;top:8%;width:2px;height:26%;background:rgba(80,70,55,.55);transform:rotate(9deg)}
.s-sofa{position:absolute;left:22%;bottom:8%;width:54%;height:24%;border-radius:12px 12px 6px 6px}
.s-sofa-new{background:linear-gradient(180deg,#3d5c74,#2c4258);box-shadow:0 12px 24px rgba(27,43,58,.28)}
.s-sofa-new::after{content:"";position:absolute;left:8%;top:-16%;width:26%;height:34%;border-radius:8px;background:var(--yellow)}
.s-sofa-old{background:linear-gradient(180deg,#7f7566,#5f584c)}
.s-plant{position:absolute;right:7%;bottom:9%;width:12%;height:30%}
.s-plant b{position:absolute;bottom:0;left:12%;width:76%;height:34%;background:#b4623a;border-radius:3px 3px 8px 8px;display:block}
.s-plant span{position:absolute;bottom:30%;left:46%;width:8px;height:60%;background:var(--green);border-radius:50% 50% 0 0;transform-origin:bottom center}
.s-plant span:nth-child(1){transform:rotate(-28deg)}
.s-plant span:nth-child(3){transform:rotate(28deg)}
.s-lamp{position:absolute;left:8%;bottom:9%;width:9%;height:34%;background:linear-gradient(180deg,var(--yellow) 0 26%,#c8ccd0 26% 100%);border-radius:8px 8px 2px 2px}
.s-box{position:absolute;left:10%;bottom:9%;width:16%;height:14%;background:#b39b74;border-radius:3px;box-shadow:inset 0 0 0 2px rgba(0,0,0,.12)}
.ba-tag{position:absolute;top:14px;font-family:var(--mono);font-size:11.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;padding:6px 11px;border-radius:999px;color:#fff}
.ba-tag-before{left:14px;background:rgba(27,43,58,.82)}
.ba-tag-after{right:14px;background:var(--green)}
.ba-divider{position:absolute;top:0;bottom:0;width:3px;background:#fff;box-shadow:0 0 0 1px rgba(27,43,58,.18);transform:translateX(-1.5px)}
.ba-handle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:46px;height:46px;border-radius:50%;background:#fff;color:var(--navy);display:grid;place-items:center;box-shadow:var(--sh-2);cursor:ew-resize;border:2px solid var(--orange)}
.ba-handle:hover{background:var(--orange);color:#fff}
figcaption{margin-top:14px;font-size:13.5px;color:var(--muted);text-align:center}
figcaption strong{font-family:var(--mono);color:var(--navy)}
/* ---------- sections ---------- */
.band{padding:76px 0}
.band-alt{background:var(--surface)}
.band-navy{background:var(--navy);color:#fff}
.sec-head{max-width:60ch;margin:0 auto 40px;text-align:center}
.sec-head-left{text-align:left;margin:0}
.sec-head h2{font-size:clamp(26px,3vw,36px)}
.sec-head p{margin-top:12px;color:var(--ink-2);font-size:16.5px}
.band-navy .sec-head p{color:#c3d1dd}
.pill{display:inline-block;font-size:12px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;padding:6px 12px;border-radius:999px;margin-bottom:14px}
.pill-yellow{background:var(--yellow-soft);color:#7a5c00}
.pill-orange{background:rgba(242,101,34,.18);color:#ffc7a8}
/* ---------- categories ---------- */
.cats{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(6,1fr);gap:16px}
.cat{
background:var(--white);border:1px solid var(--line);border-radius:var(--r-md);padding:20px 14px;
text-align:center;cursor:pointer;box-shadow:var(--sh-1);
transition:transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.cat:hover,.cat:focus-visible{transform:translateY(-5px);box-shadow:var(--sh-2);border-color:var(--orange)}
.cat-ico{width:46px;height:46px;margin:0 auto 12px;border-radius:12px;display:grid;place-items:center;background:var(--orange-soft);color:var(--orange-d)}
.cat-name{display:block;font-weight:700;font-size:15px}
.cat-count{display:block;font-family:var(--mono);font-size:12px;color:var(--muted);margin-top:4px}
/* ---------- calculator ---------- */
.calc-grid{display:grid;grid-template-columns:.85fr 1.15fr;gap:44px;align-items:start}
.ticks{list-style:none;padding:0;margin:22px 0 0;display:grid;gap:10px}
.ticks li{position:relative;padding-left:28px;font-size:14.5px;color:var(--ink-2)}
.ticks li::before{content:"✓";position:absolute;left:0;top:2px;width:19px;height:19px;border-radius:50%;
background:var(--green-soft);color:var(--green);font-weight:800;font-size:12px;
display:grid;place-items:center;line-height:1}
.calc-card{background:var(--white);border:1px solid var(--line);border-radius:var(--r-lg);padding:26px;box-shadow:var(--sh-2)}
.seg{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;border:0;margin:0 0 20px;padding:5px;background:var(--surface);border-radius:999px}
.seg label{position:relative}
.seg input{position:absolute;opacity:0;inset:0;cursor:pointer}
.seg span{display:block;text-align:center;padding:10px 6px;border-radius:999px;font-size:14px;font-weight:700;color:var(--ink-2);cursor:pointer;transition:background .18s,color .18s}
.seg label:hover span{color:var(--navy)}
.seg input:checked + span{background:var(--navy);color:#fff;box-shadow:var(--sh-1)}
.seg input:focus-visible + span{outline:3px solid var(--orange);outline-offset:2px}
.fields{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}
.field{display:flex;flex-direction:column;gap:6px}
.field > span{font-size:12.5px;font-weight:700;color:var(--ink-2);text-transform:uppercase;letter-spacing:.06em}
.field em{font-style:normal;font-family:var(--mono);color:var(--muted);text-transform:none;letter-spacing:0}
.field input,.field select{
font:600 15px/1.2 Inter,sans-serif;padding:11px 12px;border:1px solid var(--line-2);
border-radius:var(--r-sm);background:var(--white);color:var(--ink);width:100%;
}
.field input{font-family:var(--mono)}
.field input:focus,.field select:focus{border-color:var(--orange);outline:none;box-shadow:0 0 0 3px rgba(242,101,34,.2)}
.readout{display:grid;grid-template-columns:1.2fr 1fr;gap:16px;margin:22px 0 18px;padding:20px;border-radius:var(--r-md);background:var(--navy);color:#fff}
.ro-label{font-size:12px;text-transform:uppercase;letter-spacing:.09em;color:#a9bccd;font-weight:700}
.ro-value{display:block;font-family:var(--mono);font-size:38px;font-weight:700;line-height:1.05;margin:6px 0 4px;color:var(--yellow)}
.ro-sub{font-size:13px;color:#c3d1dd}
.ro-side{display:grid;gap:10px;align-content:center;border-left:1px solid rgba(255,255,255,.16);padding-left:16px}
.ro-side span{display:block;font-size:11.5px;text-transform:uppercase;letter-spacing:.08em;color:#a9bccd;font-weight:700}
.ro-side b{font-family:var(--mono);font-size:21px}
.brk{width:100%;border-collapse:collapse;font-family:var(--mono);font-size:13px}
.brk th,.brk td{padding:10px 8px;text-align:right;border-bottom:1px solid var(--line)}
.brk th:first-child,.brk td:first-child{text-align:left}
.brk thead th{font-size:11px;text-transform:uppercase;letter-spacing:.07em;color:var(--muted);border-bottom:1px solid var(--line-2)}
.brk tbody tr{transition:background .15s}
.brk tbody tr:hover{background:var(--surface)}
.brk tfoot th,.brk tfoot td{border:0;padding-top:14px;font-weight:700;font-size:15px}
.brk tfoot td{color:var(--orange-d)}
.calc-note{margin-top:14px;font-size:12.5px;color:var(--muted)}
/* ---------- guides ---------- */
.guides{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(3,1fr);gap:22px}
.guide{background:var(--white);border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;box-shadow:var(--sh-1);display:flex;flex-direction:column;transition:transform .18s ease, box-shadow .18s ease}
.guide:hover{transform:translateY(-6px);box-shadow:var(--sh-2)}
.guide-art{height:132px;position:relative;overflow:hidden}
.guide-art::after{content:"";position:absolute;inset:0;background:repeating-linear-gradient(45deg,rgba(255,255,255,.14) 0 10px,transparent 10px 20px)}
.guide-body{padding:18px 20px 20px;display:flex;flex-direction:column;gap:10px;flex:1}
.guide h3{font-size:18px}
.guide p{font-size:14px;color:var(--ink-2)}
.guide-meta{list-style:none;display:flex;flex-wrap:wrap;gap:14px;margin:auto 0 0;padding:14px 0 0;border-top:1px solid var(--line);font-family:var(--mono);font-size:12.5px;color:var(--muted)}
.guide-meta b{color:var(--navy)}
.badges{display:flex;gap:8px;flex-wrap:wrap}
.badge{font-size:11.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;padding:5px 10px;border-radius:999px;font-family:Inter,sans-serif;white-space:nowrap}
.badge-easy{background:var(--green-soft);color:#1f6b3b}
.badge-med{background:var(--yellow-soft);color:#7a5c00}
.badge-hard{background:#fde3de;color:#a53625}
.badge-permit{background:var(--navy);color:var(--yellow)}
.badge-ok{background:var(--green-soft);color:#1f6b3b}
.badge-warn{background:var(--yellow-soft);color:#7a5c00}
.badge-stop{background:#fde3de;color:#a53625}
/* ---------- safety ---------- */
.safety-grid{display:grid;grid-template-columns:.9fr 1.1fr;gap:44px;align-items:start}
.safety-grid h2{font-size:clamp(26px,3vw,34px)}
.safety-lede{margin-top:12px;color:#c3d1dd;max-width:42ch}
.meter{margin-top:26px}
.meter-bar{height:12px;border-radius:999px;background:rgba(255,255,255,.14);overflow:hidden}
.meter-bar i{display:block;height:100%;border-radius:999px;background:linear-gradient(90deg,var(--orange),var(--yellow));transition:width .35s cubic-bezier(.4,0,.2,1)}
.meter-txt{margin-top:10px;font-size:14px;color:#c3d1dd}
.meter-txt strong{font-family:var(--mono);color:#fff;margin-right:6px}
.checks{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:1fr 1fr;gap:10px}
.check{display:flex;gap:12px;align-items:flex-start;padding:14px 16px;border-radius:var(--r-md);background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.12);cursor:pointer;transition:background .16s,border-color .16s}
.check:hover{background:rgba(255,255,255,.11)}
.check input{width:20px;height:20px;margin:1px 0 0;accent-color:var(--orange);flex:none;cursor:pointer}
.check-txt{font-size:14.5px;font-weight:600}
.check-sub{display:block;font-weight:400;font-size:12.5px;color:#a9bccd;margin-top:3px}
.check.done .check-txt{color:#9fe0b5;text-decoration:line-through;text-decoration-color:rgba(159,224,181,.5)}
/* ---------- compare table ---------- */
.tbl-scroll{overflow-x:auto;border-radius:var(--r-lg);border:1px solid var(--line);background:var(--white);box-shadow:var(--sh-1)}
.cmp{width:100%;border-collapse:collapse;min-width:760px}
.cmp th,.cmp td{padding:15px 18px;text-align:right;border-bottom:1px solid var(--line);font-family:var(--mono);font-size:14px}
.cmp thead th{font-family:Inter,sans-serif;font-size:12px;text-transform:uppercase;letter-spacing:.07em;color:var(--muted);background:var(--surface)}
.cmp th:first-child,.cmp td:first-child{text-align:left}
.cmp tbody th{font-family:Inter,sans-serif;font-weight:700;font-size:15px}
.cmp tbody tr:last-child th,.cmp tbody tr:last-child td{border-bottom:0}
.cmp tbody tr:hover{background:var(--yellow-soft)}
.cmp .save{color:var(--green);font-weight:700}
.cmp td:last-child{text-align:right}
/* ---------- pro strip ---------- */
.pro-strip{display:grid;grid-template-columns:1.1fr .9fr;gap:34px;align-items:center;padding:38px;border-radius:var(--r-lg);
background:linear-gradient(135deg,var(--navy),var(--navy-l));color:#fff;box-shadow:var(--sh-2);position:relative;overflow:hidden}
.pro-strip::after{content:"";position:absolute;right:-60px;top:-60px;width:260px;height:260px;border-radius:50%;background:radial-gradient(circle,rgba(245,197,24,.28),transparent 68%)}
.pro-copy h2{font-size:clamp(22px,2.5vw,30px)}
.pro-copy p{margin-top:10px;color:#c3d1dd;font-size:15.5px;max-width:44ch}
.pro-form{display:flex;gap:10px;flex-wrap:wrap;position:relative;z-index:1}
.pro-form input{
flex:1 1 180px;min-width:0;font:600 16px/1.2 var(--mono);padding:14px 16px;border-radius:999px;
border:1px solid rgba(255,255,255,.28);background:rgba(255,255,255,.1);color:#fff;letter-spacing:.12em;
}
.pro-form input::placeholder{color:#9fb2c2;letter-spacing:0;font-family:Inter,sans-serif;font-weight:500}
.pro-form input:focus{outline:none;border-color:var(--yellow);box-shadow:0 0 0 3px rgba(245,197,24,.25)}
.pro-form input[aria-invalid="true"]{border-color:var(--danger);box-shadow:0 0 0 3px rgba(212,80,62,.28)}
.pro-help{flex-basis:100%;font-size:12.5px;color:#a9bccd}
/* ---------- footer / toast ---------- */
.foot{border-top:1px solid var(--line);padding:26px 0;background:var(--surface)}
.foot-in{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;font-size:13px;color:var(--muted)}
.toast-host{position:fixed;left:50%;bottom:26px;transform:translateX(-50%);z-index:80;display:grid;gap:8px;width:min(420px,calc(100% - 32px))}
.toast{background:var(--navy);color:#fff;padding:13px 18px;border-radius:var(--r-md);box-shadow:var(--sh-3);font-size:14.5px;font-weight:600;
display:flex;gap:10px;align-items:center;animation:pop .28s cubic-bezier(.2,.9,.3,1.2)}
.toast::before{content:"";width:9px;height:9px;border-radius:50%;background:var(--yellow);flex:none}
.toast.err::before{background:var(--danger)}
.toast.out{opacity:0;transform:translateY(10px);transition:opacity .25s,transform .25s}
@keyframes pop{from{opacity:0;transform:translateY(14px) scale(.97)}to{opacity:1;transform:none}}
/* ---------- responsive ---------- */
@media (max-width:1040px){
.hero-in,.calc-grid,.safety-grid,.pro-strip{grid-template-columns:1fr}
.cats{grid-template-columns:repeat(3,1fr)}
.guides{grid-template-columns:repeat(2,1fr)}
.nav-links{display:none}
}
@media (max-width:760px){
.guides{grid-template-columns:1fr}
.fields{grid-template-columns:1fr 1fr}
.checks{grid-template-columns:1fr}
}
@media (max-width:520px){
.wrap{width:calc(100% - 28px)}
.band{padding:52px 0}
.hero{padding:40px 0 52px}
.cats{grid-template-columns:repeat(2,1fr)}
.seg{grid-template-columns:1fr;border-radius:var(--r-md)}
.seg span{border-radius:var(--r-sm)}
.seg input:checked + span{border-radius:var(--r-sm)}
.readout{grid-template-columns:1fr}
.ro-side{border-left:0;border-top:1px solid rgba(255,255,255,.16);padding:14px 0 0;grid-template-columns:1fr 1fr;display:grid}
.calc-card{padding:18px}
.pro-strip{padding:24px}
.hero-stats{gap:18px}
.brk{font-size:12px}
.brk th,.brk td{padding:8px 5px}
.ba-handle{width:40px;height:40px}
}
@media (prefers-reduced-motion:reduce){
html{scroll-behavior:auto}
*,*::before,*::after{animation-duration:.001ms !important;animation-iteration-count:1 !important;transition-duration:.001ms !important}
}/* Weekend Reno — DIY home improvement landing (vanilla JS) */
(function () {
"use strict";
/* ---------- toast ---------- */
var host = document.getElementById("toastHost");
function toast(msg, kind) {
var el = document.createElement("div");
el.className = "toast" + (kind === "err" ? " err" : "");
el.textContent = msg;
host.appendChild(el);
setTimeout(function () {
el.classList.add("out");
setTimeout(function () { el.remove(); }, 300);
}, 3400);
}
var usd = function (n) {
return "$" + Math.round(n).toLocaleString("en-US");
};
/* ---------- before / after slider ---------- */
var frame = document.getElementById("baFrame");
var before = document.getElementById("baBefore");
var divider = document.getElementById("baDivider");
var handle = document.getElementById("baHandle");
var pos = 52;
function setPos(p) {
pos = Math.max(0, Math.min(100, p));
before.style.clipPath = "inset(0 " + (100 - pos) + "% 0 0)";
divider.style.left = pos + "%";
handle.setAttribute("aria-valuenow", Math.round(pos));
handle.setAttribute("aria-valuetext", Math.round(pos) + "% after");
}
setPos(pos);
function fromEvent(e) {
var r = frame.getBoundingClientRect();
var x = (e.touches ? e.touches[0].clientX : e.clientX) - r.left;
setPos((x / r.width) * 100);
}
var dragging = false;
function down(e) { dragging = true; frame.setPointerCapture && e.pointerId != null && frame.setPointerCapture(e.pointerId); fromEvent(e); }
function move(e) { if (dragging) { fromEvent(e); e.preventDefault(); } }
function up() { dragging = false; }
frame.addEventListener("pointerdown", down);
window.addEventListener("pointermove", move, { passive: false });
window.addEventListener("pointerup", up);
window.addEventListener("pointercancel", up);
handle.addEventListener("keydown", function (e) {
var step = e.shiftKey ? 10 : 2;
var map = { ArrowLeft: -step, ArrowRight: step, Home: -100, End: 100, PageDown: -10, PageUp: 10 };
if (e.key in map) { setPos(e.key === "Home" ? 0 : e.key === "End" ? 100 : pos + map[e.key]); e.preventDefault(); }
});
/* ---------- categories ---------- */
var ICONS = {
paint: '<path d="M4 5h13a2 2 0 0 1 2 2v3H4z"/><path d="M15 10v3a2 2 0 0 1-2 2h-2v6" fill="none" stroke="currentColor" stroke-width="2"/>',
tile: '<rect x="3" y="3" width="8" height="8" rx="1"/><rect x="13" y="3" width="8" height="8" rx="1"/><rect x="3" y="13" width="8" height="8" rx="1"/><rect x="13" y="13" width="8" height="8" rx="1"/>',
deck: '<rect x="2" y="5" width="20" height="3" rx="1"/><rect x="2" y="10.5" width="20" height="3" rx="1"/><rect x="2" y="16" width="20" height="3" rx="1"/>',
bath: '<path d="M3 12h18v3a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4z"/><path d="M7 12V6a2 2 0 0 1 4 0" fill="none" stroke="currentColor" stroke-width="2"/>',
kitchen: '<rect x="3" y="3" width="18" height="18" rx="2" fill="none" stroke="currentColor" stroke-width="2"/><path d="M3 10h18M8 5.5v2.5" stroke="currentColor" stroke-width="2" fill="none"/>',
electrical: '<path d="M13 2 4 14h6l-1 8 9-12h-6z"/>'
};
var CATS = [
{ id: "paint", name: "Paint", n: 96 },
{ id: "tile", name: "Tile", n: 54 },
{ id: "deck", name: "Deck", n: 38 },
{ id: "bath", name: "Bath", n: 71 },
{ id: "kitchen", name: "Kitchen", n: 83 },
{ id: "electrical", name: "Electrical", n: 70 }
];
var catList = document.getElementById("cats");
CATS.forEach(function (c) {
var li = document.createElement("li");
li.className = "cat";
li.tabIndex = 0;
li.setAttribute("role", "button");
li.setAttribute("aria-label", c.name + " — " + c.n + " guides");
li.innerHTML =
'<span class="cat-ico" aria-hidden="true"><svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor">' + ICONS[c.id] + "</svg></span>" +
'<span class="cat-name">' + c.name + "</span>" +
'<span class="cat-count">' + c.n + " guides</span>";
function open() { toast(c.name + ": " + c.n + " guides ready to browse."); }
li.addEventListener("click", open);
li.addEventListener("keydown", function (e) {
if (e.key === "Enter" || e.key === " ") { e.preventDefault(); open(); }
});
catList.appendChild(li);
});
/* ---------- materials calculator ---------- */
var PRICES = {
paint: { gal: 42, primer: 34, tape: 7.5, drop: 12, roller: 16 },
tile: { sqft: 4.6, thinset: 24, grout: 18, spacers: 9, trowel: 14 },
deck: { board: 11.4, joist: 15.2, post: 22, hardware: 96, stain: 46 }
};
var els = {
form: document.getElementById("calcForm"),
len: document.getElementById("fLen"),
wid: document.getElementById("fWid"),
hgt: document.getElementById("fHgt"),
qual: document.getElementById("fQual"),
hField: document.getElementById("hField"),
label: document.getElementById("roLabel"),
value: document.getElementById("roValue"),
sub: document.getElementById("roSub"),
cost: document.getElementById("roCost"),
hours: document.getElementById("roHours"),
body: document.getElementById("brkBody"),
total: document.getElementById("brkTotal")
};
function num(input, fallback) {
var v = parseFloat(input.value);
if (!isFinite(v) || v <= 0) return fallback;
return Math.min(v, parseFloat(input.max) || v);
}
function proj() {
var r = document.querySelector('input[name="proj"]:checked');
return r ? r.value : "paint";
}
function compute() {
var p = proj();
var L = num(els.len, 16), W = num(els.wid, 12), H = num(els.hgt, 9);
var q = parseFloat(els.qual.value) || 1;
var area = L * W;
var rows = [], headline = "", sub = "", hours = 0;
els.hField.style.display = p === "paint" ? "" : "none";
if (p === "paint") {
var wall = 2 * (L + W) * H;
var paintArea = wall * 2; // two coats
var gal = Math.ceil(paintArea / 350);
var primerGal = Math.ceil(wall / 400);
headline = gal + " gal";
els.label.textContent = "Paint needed";
sub = "2 coats · " + Math.round(wall) + " sq ft of wall";
hours = Math.round(wall / 55 + 3);
rows = [
["Interior paint · eggshell", gal, PRICES.paint.gal * q, "gal"],
["Bonding primer", primerGal, PRICES.paint.primer * q, "gal"],
["Painter's tape 1.88in", Math.ceil(2 * (L + W) / 60), PRICES.paint.tape, "roll"],
["Canvas drop cloth", Math.ceil(area / 90), PRICES.paint.drop, "ea"],
["Roller + tray kit", 1, PRICES.paint.roller, "kit"]
];
} else if (p === "tile") {
var tiles = Math.ceil((area * 1.1) / 1); // 12x12 tile = 1 sq ft, +10% waste
headline = tiles + " tiles";
els.label.textContent = "Tiles (12×12)";
sub = Math.round(area) + " sq ft + 10% waste";
hours = Math.round(area * 0.32 + 4);
rows = [
["Porcelain tile 12×12", tiles, PRICES.tile.sqft * q, "ea"],
["Modified thinset 50 lb", Math.ceil(area / 80), PRICES.tile.thinset, "bag"],
["Sanded grout 25 lb", Math.ceil(area / 110), PRICES.tile.grout, "bag"],
["Spacers 1/8in (200 ct)", Math.ceil(tiles / 200), PRICES.tile.spacers, "bag"],
["Notched trowel + float", 1, PRICES.tile.trowel, "kit"]
];
} else {
var boards = Math.ceil((L * 12) / 5.5 * 1.1); // 5.5in deck boards + 10% waste
var joists = Math.ceil((L * 12) / 16) + 1;
var posts = Math.max(4, Math.ceil(L / 6) * 2);
headline = boards + " boards";
els.label.textContent = "Deck boards";
sub = Math.round(area) + " sq ft frame · " + joists + " joists @ 16in OC";
hours = Math.round(area * 0.26 + 14);
rows = [
["Deck board 5/4×6×" + Math.ceil(W) + "ft", boards, PRICES.deck.board * q, "ea"],
["Joist 2×8×" + Math.ceil(W) + "ft PT", joists, PRICES.deck.joist * q, "ea"],
["Post 4×4×8ft PT", posts, PRICES.deck.post, "ea"],
["Joist hangers + screws", 1, PRICES.deck.hardware + joists * 2.2, "lot"],
["Sealer / stain 1 gal", Math.ceil(area / 200), PRICES.deck.stain, "gal"]
];
}
var total = 0;
els.body.innerHTML = "";
rows.forEach(function (r) {
var qty = r[1], unit = r[2], sum = qty * unit;
total += sum;
var tr = document.createElement("tr");
tr.innerHTML =
"<td>" + r[0] + "</td>" +
"<td>" + qty + " " + r[3] + "</td>" +
"<td>" + usd(unit) + "</td>" +
"<td>" + usd(sum) + "</td>";
els.body.appendChild(tr);
});
els.value.textContent = headline;
els.sub.textContent = sub;
els.cost.textContent = usd(total);
els.hours.textContent = hours + " h";
els.total.textContent = usd(total);
}
els.form.addEventListener("input", compute);
els.form.addEventListener("change", compute);
els.form.addEventListener("submit", function (e) { e.preventDefault(); });
compute();
/* ---------- guide cards ---------- */
var GUIDES = [
{ t: "Repaint a bedroom in one weekend", d: "Cut in like a pro, roll a flawless second coat and get crisp ceiling lines without tape.", skill: "easy", cost: "$180–$310", time: "9 h", permit: false, g: "linear-gradient(135deg,#f26522,#f5c518)" },
{ t: "Lay a herringbone tile entry", d: "Layout math, a dry run, and the thinset trick that keeps 45° cuts from creeping.", skill: "med", cost: "$340–$520", time: "14 h", permit: false, g: "linear-gradient(135deg,#2c4258,#7ba8c7)" },
{ t: "Build a 12×16 ground-level deck", d: "Footings, ledger flashing, joist spacing and the inspection stages you can't skip.", skill: "hard", cost: "$2,400–$3,100", time: "48 h", permit: true, g: "linear-gradient(135deg,#a86f3c,#c98f56)" },
{ t: "Swap a vanity and faucet", d: "Shut-offs, supply lines, silicone bead — and how to not crack the drain trap.", skill: "med", cost: "$420–$680", time: "7 h", permit: false, g: "linear-gradient(135deg,#3f9e5c,#a8dcbb)" },
{ t: "Install peel-and-stick backsplash", d: "The cheapest kitchen upgrade that still looks intentional two years later.", skill: "easy", cost: "$95–$160", time: "4 h", permit: false, g: "linear-gradient(135deg,#f5c518,#ffe9de)" },
{ t: "Add a dedicated 20A kitchen circuit", d: "Panel capacity, cable runs and why this one usually ends with a licensed hand.", skill: "hard", cost: "$150–$240", time: "6 h", permit: true, g: "linear-gradient(135deg,#1b2b3a,#3f5266)" }
];
var SKILL = { easy: ["Beginner", "badge-easy"], med: ["Intermediate", "badge-med"], hard: ["Advanced", "badge-hard"] };
var list = document.getElementById("guides-list");
GUIDES.forEach(function (g) {
var li = document.createElement("li");
li.className = "guide";
var s = SKILL[g.skill];
li.innerHTML =
'<div class="guide-art" style="background:' + g.g + '"></div>' +
'<div class="guide-body">' +
'<div class="badges"><span class="badge ' + s[1] + '">' + s[0] + "</span>" +
(g.permit ? '<span class="badge badge-permit">Permit required</span>' : "") + "</div>" +
"<h3>" + g.t + "</h3><p>" + g.d + "</p>" +
'<ul class="guide-meta"><li>Cost <b>' + g.cost + "</b></li><li>Time <b>" + g.time + "</b></li></ul>" +
"</div>";
list.appendChild(li);
});
/* ---------- safety checklist ---------- */
var TASKS = [
["Safety glasses and hearing protection on hand", "ANSI Z87.1 rated, not sunglasses"],
["Work area cleared and well lit", "Two light sources beats one bright one"],
["Breaker off and tested at the outlet", "Non-contact tester, then confirm"],
["Water shut-off located and turned", "Know where the main valve is too"],
["Utilities located before any digging", "Free locate call, 3 business days"],
["Ladder on level ground, 4:1 angle", "Belt buckle stays between the rails"],
["Dust mask or respirator for sanding", "N95 minimum; P100 for old finishes"],
["Permit checked with the local office", "Structural, electrical and gas work"]
];
var checks = document.getElementById("checks");
var fill = document.getElementById("meterFill");
var count = document.getElementById("meterCount");
var msg = document.getElementById("meterMsg");
var MSGS = ["Let's get set up.", "Good start — keep going.", "Halfway there.", "Almost cleared for work.", "All clear. Go make sawdust."];
TASKS.forEach(function (t, i) {
var li = document.createElement("li");
li.className = "check";
var id = "chk" + i;
li.innerHTML =
'<input type="checkbox" id="' + id + '">' +
'<label class="check-txt" for="' + id + '">' + t[0] + '<span class="check-sub">' + t[1] + "</span></label>";
checks.appendChild(li);
});
function refreshMeter() {
var boxes = checks.querySelectorAll('input[type="checkbox"]');
var done = 0;
boxes.forEach(function (b) {
b.closest(".check").classList.toggle("done", b.checked);
if (b.checked) done++;
});
var pct = (done / boxes.length) * 100;
fill.style.width = pct + "%";
count.textContent = done + " of " + boxes.length;
msg.textContent = MSGS[Math.min(MSGS.length - 1, Math.floor(pct / 25))];
if (done === boxes.length) toast("Checklist complete — you're cleared to start.");
}
checks.addEventListener("change", refreshMeter);
refreshMeter();
/* ---------- pro finder ---------- */
var proForm = document.getElementById("proForm");
var zip = document.getElementById("zip");
var PROS = ["Hollis & Vane Contracting", "Marlow Brothers Build", "Cedarline Home Services", "Ridgeway Trades Co."];
proForm.addEventListener("submit", function (e) {
e.preventDefault();
var v = zip.value.trim();
if (!/^\d{5}$/.test(v)) {
zip.setAttribute("aria-invalid", "true");
zip.focus();
toast("Enter a valid 5-digit ZIP code.", "err");
return;
}
zip.removeAttribute("aria-invalid");
var a = PROS[Math.floor(Math.random() * PROS.length)];
toast("3 licensed pros near " + v + " — starting with " + a + ".");
zip.value = "";
});
zip.addEventListener("input", function () {
zip.value = zip.value.replace(/\D/g, "").slice(0, 5);
zip.removeAttribute("aria-invalid");
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Weekend Reno — DIY Home Improvement Guides</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<a class="skip" href="#main">Skip to content</a>
<header class="nav">
<div class="wrap nav-in">
<a class="brand" href="#main">
<span class="brand-mark" aria-hidden="true">
<svg viewBox="0 0 32 32" width="22" height="22"><path d="M4 18 L16 7 L28 18" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><rect x="10" y="18" width="12" height="9" rx="1.5" fill="currentColor"/></svg>
</span>
<span class="brand-txt">Weekend<em>Reno</em></span>
</a>
<nav class="nav-links" aria-label="Primary">
<a href="#categories">Projects</a>
<a href="#calc">Calculator</a>
<a href="#guides">Guides</a>
<a href="#safety">Safety</a>
<a href="#compare">DIY vs Pro</a>
</nav>
<a class="btn btn-primary btn-sm" href="#pro">Find a Pro</a>
</div>
</header>
<main id="main">
<!-- HERO -->
<section class="hero">
<div class="wrap hero-in">
<div class="hero-copy">
<span class="eyebrow"><span class="dot"></span> 412 step-by-step guides · updated weekly</span>
<h1>Fix it Saturday.<br>Show it off Sunday.</h1>
<p class="lede">Honest instructions, real material lists and a calculator that tells you exactly how much paint, tile or lumber to buy — before you load the car.</p>
<div class="hero-cta">
<a class="btn btn-primary" href="#calc">Estimate my project</a>
<a class="btn btn-ghost" href="#guides">Browse guides</a>
</div>
<ul class="hero-stats">
<li><strong>$1,840</strong><span>avg. saved per reno</span></li>
<li><strong>2.5</strong><span>weekends, typical</span></li>
<li><strong>96%</strong><span>finished what they started</span></li>
</ul>
</div>
<figure class="ba" aria-labelledby="ba-cap">
<div class="ba-frame" id="baFrame">
<div class="scene scene-after" aria-hidden="true">
<div class="s-wall"></div><div class="s-floor s-floor-new"></div>
<div class="s-window"><i></i><i></i></div>
<div class="s-art a1"></div><div class="s-art a2"></div>
<div class="s-plant"><span></span><span></span><span></span><b></b></div>
<div class="s-sofa s-sofa-new"></div>
<div class="s-lamp"></div>
</div>
<div class="scene scene-before" id="baBefore" aria-hidden="true">
<div class="s-wall s-wall-old"></div><div class="s-floor s-floor-old"></div>
<div class="s-window s-window-old"><i></i><i></i></div>
<div class="s-patch p1"></div><div class="s-patch p2"></div><div class="s-crack"></div>
<div class="s-sofa s-sofa-old"></div>
<div class="s-box"></div>
</div>
<span class="ba-tag ba-tag-before">Before</span>
<span class="ba-tag ba-tag-after">After</span>
<div class="ba-divider" id="baDivider" style="left:52%">
<div class="ba-handle" id="baHandle" role="slider" tabindex="0"
aria-label="Before and after comparison" aria-valuemin="0" aria-valuemax="100"
aria-valuenow="52" aria-valuetext="52% after">
<svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path d="M9 6 4 12l5 6M15 6l5 6-5 6" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
</div>
</div>
<figcaption id="ba-cap">Living room refresh · paint + LVP flooring · <strong>$1,210</strong> materials, 3 weekends</figcaption>
</figure>
</div>
</section>
<!-- CATEGORIES -->
<section id="categories" class="band">
<div class="wrap">
<div class="sec-head">
<h2>Pick a project</h2>
<p>Every guide includes a cut list, tool list and the mistakes people actually make.</p>
</div>
<ul class="cats" id="cats"></ul>
</div>
</section>
<!-- CALCULATOR -->
<section id="calc" class="band band-alt">
<div class="wrap calc-grid">
<div class="sec-head sec-head-left">
<span class="pill pill-yellow">Featured tool</span>
<h2>Materials calculator</h2>
<p>Enter the room once. We size the order, add waste, and give you a realistic weekend-hours estimate at store prices from Fairbrook Supply.</p>
<ul class="ticks">
<li>10% waste factor baked into tile & boards</li>
<li>Two coats assumed for paint, doors and trim excluded</li>
<li>Prices refresh with the regional index each Monday</li>
</ul>
</div>
<form class="calc-card" id="calcForm" novalidate>
<fieldset class="seg" id="projSeg">
<legend class="sr-only">Project type</legend>
<label><input type="radio" name="proj" value="paint" checked><span>Paint a room</span></label>
<label><input type="radio" name="proj" value="tile"><span>Tile a floor</span></label>
<label><input type="radio" name="proj" value="deck"><span>Build a deck</span></label>
</fieldset>
<div class="fields">
<label class="field"><span>Length <em>ft</em></span><input id="fLen" type="number" min="2" max="80" step="0.5" value="16"></label>
<label class="field"><span>Width <em>ft</em></span><input id="fWid" type="number" min="2" max="80" step="0.5" value="12"></label>
<label class="field" id="hField"><span>Wall height <em>ft</em></span><input id="fHgt" type="number" min="6" max="16" step="0.5" value="9"></label>
<label class="field" id="qField"><span>Finish quality</span>
<select id="fQual">
<option value="0.85">Builder grade</option>
<option value="1" selected>Mid grade</option>
<option value="1.35">Premium</option>
</select>
</label>
</div>
<div class="readout">
<div class="ro-main">
<span class="ro-label" id="roLabel">Paint needed</span>
<strong class="ro-value" id="roValue">4 gal</strong>
<span class="ro-sub" id="roSub">2 coats · 384 sq ft of wall</span>
</div>
<div class="ro-side">
<div><span>Materials</span><b id="roCost">$248</b></div>
<div><span>Weekend hours</span><b id="roHours">9 h</b></div>
</div>
</div>
<table class="brk">
<caption class="sr-only">Cost breakdown</caption>
<thead><tr><th scope="col">Item</th><th scope="col">Qty</th><th scope="col">Unit</th><th scope="col">Subtotal</th></tr></thead>
<tbody id="brkBody"></tbody>
<tfoot><tr><th scope="row" colspan="3">Estimated total</th><td id="brkTotal">$248</td></tr></tfoot>
</table>
<p class="calc-note">Estimates only — measure twice, and confirm quantities at the counter.</p>
</form>
</div>
</section>
<!-- GUIDES -->
<section id="guides" class="band">
<div class="wrap">
<div class="sec-head">
<h2>Popular guides this month</h2>
<p>Filtered by what readers actually completed, not what got clicked.</p>
</div>
<ul class="guides" id="guides-list"></ul>
</div>
</section>
<!-- SAFETY -->
<section id="safety" class="band band-navy">
<div class="wrap safety-grid">
<div>
<span class="pill pill-orange">Before you start</span>
<h2>Safety checklist</h2>
<p class="safety-lede">Tick these off before the first cut. Your progress is remembered while you browse.</p>
<div class="meter">
<div class="meter-bar"><i id="meterFill" style="width:0%"></i></div>
<p class="meter-txt"><strong id="meterCount">0 of 8</strong> <span id="meterMsg">Let's get set up.</span></p>
</div>
</div>
<ul class="checks" id="checks"></ul>
</div>
</section>
<!-- COMPARE -->
<section id="compare" class="band band-alt">
<div class="wrap">
<div class="sec-head">
<h2>Do it yourself, or hire it out?</h2>
<p>Median figures from 3,900 reader-submitted projects in the Fairbrook metro area.</p>
</div>
<div class="tbl-scroll">
<table class="cmp">
<caption class="sr-only">DIY versus hiring a professional</caption>
<thead><tr>
<th scope="col">Project</th><th scope="col">DIY materials</th><th scope="col">Pro quote</th>
<th scope="col">You save</th><th scope="col">Your time</th><th scope="col">Verdict</th>
</tr></thead>
<tbody>
<tr><th scope="row">Repaint a 12×16 bedroom</th><td>$248</td><td>$980</td><td class="save">$732</td><td>9 h</td><td><span class="badge badge-ok">Great DIY</span></td></tr>
<tr><th scope="row">Tile a 40 sq ft entry</th><td>$412</td><td>$1,350</td><td class="save">$938</td><td>14 h</td><td><span class="badge badge-ok">Great DIY</span></td></tr>
<tr><th scope="row">Build a 12×16 deck</th><td>$2,640</td><td>$7,900</td><td class="save">$5,260</td><td>48 h</td><td><span class="badge badge-warn">Permit first</span></td></tr>
<tr><th scope="row">Swap a vanity + faucet</th><td>$540</td><td>$1,180</td><td class="save">$640</td><td>7 h</td><td><span class="badge badge-ok">Great DIY</span></td></tr>
<tr><th scope="row">Add a 20A kitchen circuit</th><td>$190</td><td>$720</td><td class="save">$530</td><td>6 h</td><td><span class="badge badge-stop">Hire a pro</span></td></tr>
<tr><th scope="row">Move a load-bearing wall</th><td>—</td><td>$9,400</td><td class="save">—</td><td>—</td><td><span class="badge badge-stop">Hire a pro</span></td></tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- PRO FINDER -->
<section id="pro" class="band">
<div class="wrap">
<div class="pro-strip">
<div class="pro-copy">
<h2>Some jobs deserve a licensed pro</h2>
<p>Gas, structural and panel work included. We match you with three vetted contractors — no calls until you say go.</p>
</div>
<form class="pro-form" id="proForm" novalidate>
<label class="sr-only" for="zip">ZIP code</label>
<input id="zip" name="zip" inputmode="numeric" autocomplete="postal-code" maxlength="5" placeholder="ZIP code" aria-describedby="zipHelp">
<button class="btn btn-primary" type="submit">Match me</button>
<p class="pro-help" id="zipHelp">5 digits · we never sell your number.</p>
</form>
</div>
</div>
</section>
</main>
<footer class="foot">
<div class="wrap foot-in">
<p>Weekend Reno · a fictional guide site by Fairbrook Media</p>
<p class="mono">MIT · demo content · no real prices</p>
</div>
</footer>
<div class="toast-host" id="toastHost" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Home Improvement Landing
“Weekend Reno” is a marketing landing for a fictional DIY guide site. The hero pairs a confident headline with a before/after comparison of a living room — both scenes drawn entirely in CSS (walls, floorboards, window mullions, sofa, plant, stains and a hairline crack) so there are no image requests at all. Drag the divider, or focus the handle and use the arrow keys, Home/End and PageUp/PageDown; it reports its position through role="slider" and a live aria-valuetext.
The centerpiece is the materials calculator. Choose paint a room, tile a floor or build a deck, type the dimensions and finish grade, and everything downstream recalculates on each keystroke: the big headline figure (gallons, tile count, board count), estimated materials cost, weekend hours, and a monospaced breakdown table with quantities, unit prices and subtotals. Paint assumes two coats over the wall area, tile and decking carry a 10% waste factor, and deck framing derives joists at 16in on-center from the span.
Below that sit category tiles with guide counts, project cards carrying skill badges and a permit-required flag, a safety checklist band whose gradient meter and encouragement text track completion, a scrollable DIY-versus-hire comparison table, and a ZIP-code pro finder that validates input and answers with a toast. All figures are invented.
How this look is built
The illustrations lean on layered CSS gradients — repeating-linear-gradient for floorboards and grout-like plank seams, radial-gradient for the ambient hero wash — instead of exported bitmaps, so the whole page ships as three text files and scales crisply at any density. The comparison wipe uses clip-path: inset() driven from a Pointer Events handler, which is smoother and cheaper than the old two-absolute-divs-plus-width trick because the clip is composited and the layout never reflows; pointer capture also means a fast drag off the frame does not drop the gesture. The rest is custom properties for the palette, aspect-ratio to hold the hero frame, and a single width transition on the safety meter. Watch two things: clip-path updates should stay in a plain pointer handler (no per-frame layout reads), and the meter fill plus toast pop are both disabled under prefers-reduced-motion, which the stylesheet honors globally.
Illustrative UI only — always follow real safety guidance when working with tools, electronics, or repairs.