CodeAntidote's picture
Rename platform to Slab to Sale
8d5c8f9 verified
Raw History Blame Contribute Delete
77.4 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Slab to Sale | Know Before You Build</title>
<style>
:root{
--bg:#ffffff;
--soft:#f5f5f7;
--soft2:#fafafa;
--ink:#1d1d1f;
--muted:#6e6e73;
--line:#e5e5e7;
--blue:#0071e3;
--blue2:#2997ff;
--red:#c81e1e;
--orange:#e86f00;
--green:#16803c;
--shadow:0 18px 55px rgba(0,0,0,.07);
--radius:28px;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
margin:0;
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
color:var(--ink);
background:var(--bg);
-webkit-font-smoothing:antialiased;
}
button,input,select{font:inherit}
button{cursor:pointer}
a{color:inherit;text-decoration:none}
.shell{max-width:1200px;margin:auto;padding:0 22px}
/* ───── Primary navigation ───── */
.primary-nav{
height:49px;
position:sticky;
top:0;
z-index:100;
backdrop-filter:saturate(180%) blur(20px);
-webkit-backdrop-filter:saturate(180%) blur(20px);
background:rgba(255,255,255,.82);
border-bottom:1px solid rgba(0,0,0,.055);
}
.primary-nav .inner{
height:100%;
max-width:1200px;
margin:auto;
padding:0 22px;
display:flex;
align-items:center;
justify-content:space-between;
gap:16px;
}
.logo{
display:flex;align-items:center;gap:9px;
font-size:13px;font-weight:700;letter-spacing:-.01em;
white-space:nowrap
}
.logo-mark{
width:26px;height:26px;border-radius:8px;
display:grid;place-items:center;
background:#eef6ff
}
.nav-links{display:flex;align-items:center;gap:20px}
.nav-links button{
border:0;background:transparent;color:#333336;font-size:12px;padding:7px 0
}
.analyze-pill{
border:0!important;
background:var(--red)!important;
color:white!important;
padding:7px 13px!important;
border-radius:999px!important;
font-weight:650
}
/* faceted copyright badge */
.creator-badge{
display:flex;align-items:center;gap:7px;
padding:4px 8px 4px 5px;
border:1px solid rgba(0,113,227,.15);
background:linear-gradient(135deg,rgba(255,255,255,.96),rgba(239,247,255,.92));
border-radius:999px;
box-shadow:0 4px 18px rgba(0,113,227,.08);
color:#4b4b50;
font-size:9px;
line-height:1.05;
letter-spacing:.015em;
white-space:nowrap
}
.creator-gem{
width:25px;height:25px;
clip-path:polygon(25% 4%,75% 4%,98% 50%,75% 96%,25% 96%,2% 50%);
background:
linear-gradient(140deg,rgba(255,255,255,.8) 0 18%,transparent 19%),
linear-gradient(315deg,#0d5bb9,#2997ff 58%,#88c9ff);
display:grid;place-items:center;
color:#fff;font-size:8px;font-weight:800;
box-shadow:inset 0 0 0 1px rgba(255,255,255,.35)
}
.creator-badge strong{font-size:9.5px;color:#242428}
.creator-badge span{display:block;color:#7b7b80}
/* ───── Module subnav ───── */
.module-nav{
background:rgba(250,250,252,.95);
border-bottom:1px solid #ececef;
}
.module-row{
max-width:920px;margin:auto;
display:flex;justify-content:center;gap:8px;
padding:10px 12px 9px;
overflow-x:auto;
scrollbar-width:none
}
.module-row::-webkit-scrollbar{display:none}
.module{
flex:0 0 104px;
border:0;background:transparent;
text-align:center;color:#37373a;
padding:5px 4px;border-radius:14px;
transition:.2s ease
}
.module:hover{background:#fff}
.module svg{display:block;margin:0 auto 4px}
.module .m-title{display:block;font-size:10px;font-weight:650}
.module .m-sub{display:block;font-size:8px;color:#8e8e93;margin-top:1px}
/* ───── Hero ───── */
.hero{
padding:86px 0 110px;
text-align:center;
overflow:hidden;
}
.hero-eyebrow{font-size:18px;font-weight:600;color:var(--red);margin-bottom:11px}
.hero h1{
margin:0 auto;
max-width:980px;
font-size:clamp(54px,8.5vw,104px);
line-height:.93;
letter-spacing:-.065em;
font-weight:750
}
.hero-copy{
max-width:730px;
margin:24px auto 0;
color:var(--muted);
font-size:20px;line-height:1.45;
letter-spacing:-.02em
}
.hero-links{
display:flex;justify-content:center;gap:28px;flex-wrap:wrap;
margin-top:25px
}
.text-link{
border:0;background:transparent;color:var(--blue);
font-size:17px;padding:4px
}
.text-link:after{content:" ›";font-size:20px}
.house-stage{
max-width:920px;
height:480px;
margin:50px auto 0;
position:relative;
}
.hero-house{width:100%;height:100%;filter:drop-shadow(0 30px 26px rgba(0,0,0,.08))}
.hero-float{
position:absolute;
border-radius:999px;
background:rgba(255,255,255,.82);
backdrop-filter:blur(14px);
border:1px solid rgba(0,0,0,.08);
padding:9px 13px;
font-size:11px;
color:#6d6d72
}
.hero-float b{color:var(--ink);font-size:13px}
.hf1{left:4%;bottom:20%}.hf2{right:5%;top:16%}
/* ───── Motion 3D printed house ───── */
.motion-house-wrap{
position:relative;
width:100%;
height:100%;
display:grid;
place-items:center;
perspective:1200px;
}
.motion-house-svg{
width:100%;
height:100%;
overflow:visible;
filter:drop-shadow(0 30px 28px rgba(0,0,0,.10));
}
.mh-base{
opacity:1;
}
.mh-layer{
fill:none;
stroke:#c81e1e;
stroke-width:6;
stroke-linecap:round;
stroke-linejoin:round;
stroke-dasharray:850;
stroke-dashoffset:850;
opacity:.2;
animation:mh-print 8s ease-in-out infinite;
}
.mh-l2{animation-delay:.18s}
.mh-l3{animation-delay:.36s}
.mh-l4{animation-delay:.54s}
.mh-l5{animation-delay:.72s}
.mh-l6{animation-delay:.90s}
.mh-l7{animation-delay:1.08s}
.mh-l8{animation-delay:1.26s}
@keyframes mh-print{
0%,8%{stroke-dashoffset:850;opacity:.12}
40%,72%{stroke-dashoffset:0;opacity:1}
88%,100%{stroke-dashoffset:0;opacity:.28}
}
.mh-side-layer{
fill:none;
stroke:#9f1717;
stroke-width:5;
stroke-linecap:round;
stroke-dasharray:500;
stroke-dashoffset:500;
opacity:.14;
animation:mh-sideprint 8s ease-in-out infinite;
}
.mh-s2{animation-delay:.22s}
.mh-s3{animation-delay:.44s}
.mh-s4{animation-delay:.66s}
.mh-s5{animation-delay:.88s}
@keyframes mh-sideprint{
0%,12%{stroke-dashoffset:500;opacity:.08}
46%,72%{stroke-dashoffset:0;opacity:.9}
88%,100%{stroke-dashoffset:0;opacity:.24}
}
.mh-nozzle{
transform-box:fill-box;
transform-origin:center;
animation:mh-nozzle 8s cubic-bezier(.45,.05,.55,.95) infinite;
}
@keyframes mh-nozzle{
0%{transform:translate(-155px,54px)}
10%{transform:translate(145px,54px)}
20%{transform:translate(-155px,37px)}
30%{transform:translate(145px,37px)}
40%{transform:translate(-155px,20px)}
50%{transform:translate(145px,20px)}
60%{transform:translate(-155px,3px)}
70%{transform:translate(145px,3px)}
78%,100%{transform:translate(0,-80px)}
}
.mh-gantry{
transform-box:fill-box;
transform-origin:center;
animation:mh-gantry 8s ease-in-out infinite;
}
@keyframes mh-gantry{
0%,70%{opacity:1;transform:translateY(0)}
80%,100%{opacity:0;transform:translateY(-35px)}
}
.mh-roof{
opacity:0;
transform:translateY(-26px);
transform-box:fill-box;
transform-origin:center;
animation:mh-roof 8s ease-in-out infinite;
}
@keyframes mh-roof{
0%,60%{opacity:0;transform:translateY(-26px)}
72%,90%{opacity:1;transform:translateY(0)}
100%{opacity:.9;transform:translateY(0)}
}
.mh-openings{
opacity:0;
animation:mh-openings 8s ease-in-out infinite;
}
@keyframes mh-openings{
0%,68%{opacity:0}
78%,96%{opacity:1}
100%{opacity:.9}
}
.mh-mep{
opacity:0;
stroke-dasharray:220;
stroke-dashoffset:220;
animation:mh-mep 8s ease-in-out infinite;
}
@keyframes mh-mep{
0%,70%{opacity:0;stroke-dashoffset:220}
80%,96%{opacity:1;stroke-dashoffset:0}
100%{opacity:.85;stroke-dashoffset:0}
}
.mh-glow{
opacity:.0;
animation:mh-glow 8s ease-in-out infinite;
}
@keyframes mh-glow{
0%,73%{opacity:0}
86%,96%{opacity:.75}
100%{opacity:.25}
}
.mh-status{
position:absolute;
left:50%;
bottom:5%;
transform:translateX(-50%);
display:inline-flex;
align-items:center;
gap:8px;
padding:8px 12px;
border-radius:999px;
background:rgba(255,255,255,.88);
backdrop-filter:blur(16px);
-webkit-backdrop-filter:blur(16px);
border:1px solid rgba(0,0,0,.08);
font-size:10px;
color:#6e6e73;
white-space:nowrap;
}
.mh-status-dot{
width:7px;height:7px;border-radius:50%;
background:#c81e1e;
box-shadow:0 0 0 5px rgba(200,30,30,.08);
animation:mh-pulse 1.2s ease-in-out infinite;
}
@keyframes mh-pulse{
0%,100%{transform:scale(.85);opacity:.65}
50%{transform:scale(1.15);opacity:1}
}
.mh-status-text::after{
content:"Printing wall layers";
animation:mh-statuscopy 8s steps(1,end) infinite;
}
@keyframes mh-statuscopy{
0%,59%{content:"Printing wall layers"}
60%,74%{content:"Setting roof structure"}
75%,87%{content:"Adding MEP + openings"}
88%,100%{content:"House complete"}
}
@media(prefers-reduced-motion:reduce){
.mh-layer,.mh-side-layer,.mh-nozzle,.mh-gantry,.mh-roof,.mh-openings,.mh-mep,.mh-glow,.mh-status-dot,.mh-status-text::after{
animation:none!important;
}
.mh-layer,.mh-side-layer{stroke-dashoffset:0!important;opacity:1!important}
.mh-roof,.mh-openings,.mh-mep{opacity:1!important;transform:none!important;stroke-dashoffset:0!important}
.mh-gantry{opacity:.35!important}
.mh-status-text::after{content:"Finished house"!important}
}
/* ───── Shared ───── */
section{scroll-margin-top:110px}
.section{
padding:110px 0
}
.section.soft{background:var(--soft)}
.section-label{
font-size:14px;color:var(--red);font-weight:650;text-align:center;margin-bottom:12px
}
.section-title{
max-width:920px;margin:0 auto;
font-size:clamp(42px,6vw,72px);
line-height:1.02;
letter-spacing:-.05em;
text-align:center
}
.section-copy{
max-width:760px;margin:20px auto 0;
text-align:center;color:var(--muted);
font-size:19px;line-height:1.5
}
/* ───── Signature scroll story ───── */
.story{
position:relative;
background:#000;
color:white;
}
.story-wrap{
max-width:1240px;margin:auto;
display:grid;
grid-template-columns:1.22fr .78fr;
gap:44px;
padding:0 24px;
}
.story-stage{
position:sticky;
top:92px;
height:calc(100vh - 112px);
min-height:620px;
display:flex;
align-items:center;
justify-content:center
}
.story-svg{width:100%;max-width:770px}
.story-step{
min-height:72vh;
display:flex;
align-items:center;
padding:80px 0
}
.story-step-inner{
max-width:410px;
opacity:.32;
transform:translateY(14px);
transition:.4s ease
}
.story-step.active .story-step-inner{
opacity:1;transform:none
}
.story-kicker{font-size:14px;color:#a1a1a6;font-weight:600}
.story-step h3{
margin:10px 0 14px;
font-size:clamp(36px,4.8vw,62px);
letter-spacing:-.05em;line-height:1
}
.story-step p{color:#a1a1a6;font-size:18px;line-height:1.5}
.story-metric{
display:inline-flex;align-items:baseline;gap:8px;
margin-top:15px;color:white
}
.story-metric strong{font-size:42px;letter-spacing:-.04em}
.story-metric span{color:#a1a1a6}
.story-layer,.story-part{
transition:opacity .6s ease,transform .6s ease,filter .6s ease
}
.story-layer{opacity:.12}
.story-part{opacity:0;transform:translateY(8px)}
.story[data-step="0"] .wall1,
.story[data-step="1"] .wall1,
.story[data-step="1"] .wall2,
.story[data-step="2"] .wall1,
.story[data-step="2"] .wall2,
.story[data-step="2"] .wall3,
.story[data-step="3"] .story-layer{opacity:1}
.story[data-step="1"] .roof-part,
.story[data-step="2"] .roof-part,
.story[data-step="3"] .roof-part{opacity:1;transform:none}
.story[data-step="2"] .mep-part,
.story[data-step="3"] .mep-part{opacity:1;transform:none}
.story[data-step="3"] .finish-part{opacity:1;transform:none}
/* ───── Bento results ───── */
.bento{
display:grid;
grid-template-columns:repeat(12,1fr);
gap:16px;
margin-top:48px
}
.tile{
border-radius:var(--radius);
background:#fff;
padding:28px;
min-height:230px;
overflow:hidden;
position:relative;
}
.tile.softtile{background:#f5f5f7}
.tile.dark{background:#1d1d1f;color:white}
.tile.blue{background:linear-gradient(150deg,#eaf5ff,#f8fbff)}
.tile .eyebrow{font-size:13px;color:#6e6e73;font-weight:600}
.tile.dark .eyebrow{color:#aaaab0}
.tile .big{
margin-top:14px;
font-size:clamp(46px,6vw,76px);
line-height:.95;
letter-spacing:-.055em;
font-weight:730
}
.tile .big.red{color:var(--red)}
.tile .big.bluev{color:var(--blue)}
.tile .big.greenv{color:var(--green)}
.tile p{font-size:15px;line-height:1.45;color:#6e6e73;max-width:480px}
.tile.dark p{color:#aaaab0}
.tile-wide{grid-column:span 7}
.tile-mid{grid-column:span 5}
.tile-third{grid-column:span 4}
.spark{
position:absolute;right:20px;bottom:16px;width:170px;height:80px
}
/* ───── Compare ───── */
.compare-wrap{
max-width:1040px;margin:48px auto 0;
background:#fff;border-radius:30px;
overflow:hidden;border:1px solid var(--line)
}
.compare-head,.compare-row{
display:grid;grid-template-columns:1.2fr 1fr 1fr;
gap:0
}
.compare-head>div,.compare-row>div{padding:18px 22px}
.compare-head{background:#fbfbfd}
.compare-head strong{font-size:21px}
.compare-row{border-top:1px solid var(--line)}
.compare-label{color:#6e6e73;font-size:14px}
.compare-value{font-size:17px;font-weight:650}
.compare-value.print{color:var(--blue)}
.compare-value.win{color:var(--green)}
.compare-value.warn{color:var(--red)}
/* ───── Configurator ───── */
.config-layout{
max-width:1120px;margin:48px auto 0;
display:grid;grid-template-columns:1fr 350px;gap:28px;align-items:start
}
.config-main{
background:#fff;border-radius:30px;border:1px solid var(--line);overflow:hidden
}
.config-top{
padding:24px 26px;border-bottom:1px solid var(--line)
}
.progress{
display:grid;grid-template-columns:repeat(4,1fr);gap:7px;margin-top:16px
}
.progress span{height:5px;border-radius:99px;background:#e8e8ed}
.progress span.on{background:var(--blue)}
.step-pane{display:none;padding:30px}
.step-pane.active{display:block}
.step-pane h3{font-size:34px;letter-spacing:-.04em;margin:0 0 8px}
.step-pane>p{color:var(--muted);font-size:16px;line-height:1.45;margin:0 0 26px}
.choice-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:12px}
.choice{
text-align:left;
border:1px solid #d7d7db;background:#fff;border-radius:18px;
padding:18px;min-height:106px;
transition:.18s ease
}
.choice:hover{border-color:#9dcaff}
.choice.selected{border:2px solid var(--blue);background:#f6fbff}
.choice b{display:block;font-size:17px}
.choice span{display:block;font-size:13px;color:var(--muted);margin-top:6px}
.field-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.field{
border:1px solid #d9d9dd;border-radius:16px;padding:13px 14px;background:#fff
}
.field label{display:block;color:#6e6e73;font-size:11px;font-weight:650;margin-bottom:6px}
.field input,.field select{
width:100%;border:0;background:transparent;outline:0;
color:#1d1d1f;font-size:17px;font-weight:600
}
.field.full{grid-column:1/-1}
.config-actions{
padding:20px 30px 28px;
display:flex;justify-content:space-between;gap:12px
}
.config-actions button{
border:0;border-radius:999px;padding:11px 18px;font-weight:650
}
.btn-back{background:#eeeef0;color:#1d1d1f}
.btn-next{background:var(--blue);color:white}
.summary{
position:sticky;top:120px;
background:#f5f5f7;border-radius:28px;padding:24px
}
.summary h4{margin:0;font-size:20px}
.summary-project{font-size:13px;color:var(--muted);margin:5px 0 20px}
.summary-row{
display:flex;justify-content:space-between;gap:14px;
padding:12px 0;border-top:1px solid #dedee2;font-size:13px
}
.summary-row strong{text-align:right}
.summary-verdict{
margin-top:18px;background:#fff;border-radius:18px;padding:16px
}
.summary-verdict .vlabel{font-size:11px;color:#6e6e73}
.summary-verdict .vbig{
margin-top:5px;font-size:26px;font-weight:730;letter-spacing:-.035em
}
.summary-verdict .vbig.red{color:var(--red)}
.summary-verdict .vbig.green{color:var(--green)}
.summary-note{font-size:11px;color:#88888d;line-height:1.45;margin-top:10px}
/* ───── Economics detail ───── */
.econ-grid{
max-width:1100px;margin:46px auto 0;
display:grid;grid-template-columns:1fr 1fr;gap:16px
}
.econ-card{
background:#fff;border-radius:28px;padding:28px;border:1px solid var(--line)
}
.econ-card h3{font-size:25px;letter-spacing:-.035em;margin:0 0 7px}
.econ-card>p{color:var(--muted);line-height:1.5;margin:0 0 22px}
.metric-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.metric{
background:#f5f5f7;border-radius:18px;padding:17px
}
.metric span{font-size:11px;color:#6e6e73}
.metric b{display:block;font-size:26px;letter-spacing:-.035em;margin-top:5px}
.metric b.red{color:var(--red)}
.metric b.blue{color:var(--blue)}
.metric b.green{color:var(--green)}
.profit-table{width:100%;border-collapse:collapse;margin-top:18px;font-size:12px}
.profit-table th,.profit-table td{padding:11px 8px;border-bottom:1px solid #e7e7ea;text-align:right}
.profit-table th:first-child,.profit-table td:first-child{text-align:left}
.profit-table th{color:#77777d;font-size:10px;text-transform:uppercase;letter-spacing:.05em}
.positive{color:var(--green);font-weight:700}
.negative{color:var(--red);font-weight:700}
/* ───── Break-even interaction ───── */
.break-slider{
max-width:900px;margin:44px auto 0;
background:#fff;border:1px solid var(--line);border-radius:30px;padding:30px
}
.break-top{text-align:center}
.break-top .big{
font-size:clamp(52px,8vw,90px);
font-weight:740;letter-spacing:-.06em;line-height:.95
}
.break-top .big.red{color:var(--red)}
.break-top p{color:var(--muted);font-size:16px}
.break-control{
display:grid;grid-template-columns:80px 1fr 80px;gap:14px;
align-items:center;margin-top:30px
}
.break-control input[type=range]{width:100%;accent-color:var(--blue)}
.price-box{text-align:center;font-size:12px;color:#6e6e73}
.price-box b{display:block;color:#1d1d1f;font-size:15px}
.break-note{
margin-top:20px;padding:15px 17px;background:#f5f5f7;border-radius:16px;
text-align:center;color:#5f5f63;font-size:13px
}
/* ───── Footnotes ───── */
.footnotes{
max-width:1000px;margin:0 auto;
padding:80px 22px 34px;
color:#6e6e73;font-size:11px;line-height:1.5
}
.footnotes ol{padding-left:18px}
.footnotes li{margin:7px 0}
.footer{
border-top:1px solid #e7e7e9;
padding:20px 0 34px;
color:#8a8a8f;font-size:11px
}
.footer-inner{display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap}
.reveal{opacity:0;transform:translateY(16px);transition:opacity .65s ease,transform .65s ease}
.reveal.show{opacity:1;transform:none}
/* ───── Source & provenance system ───── */
.source-row{
display:flex;align-items:center;gap:7px;flex-wrap:wrap;margin-top:7px
}
.source-badge{
display:inline-flex;align-items:center;gap:5px;
border-radius:999px;padding:4px 8px;
font-size:9px;font-weight:750;letter-spacing:.045em;text-transform:uppercase;
background:#f3f3f5;color:#6e6e73;border:1px solid #e2e2e6
}
.source-badge::before{content:"";width:6px;height:6px;border-radius:50%;background:#8e8e93}
.source-badge.public::before{background:#0071e3}
.source-badge.vendor::before{background:#c81e1e}
.source-badge.user::before{background:#6e6e73}
.source-badge.estimate::before{background:#e86f00}
.source-badge.licensed::before{background:#7b61ff}
.source-badge.live::before{background:#16803c}
.source-link{
font-size:10px;color:#0071e3;font-weight:600
}
.source-link:hover{text-decoration:underline}
.benchmark-box{
margin-top:16px;padding:16px;
border:1px solid #e2e2e6;border-radius:18px;background:#fafafa
}
.benchmark-top{
display:flex;justify-content:space-between;gap:12px;align-items:flex-start;flex-wrap:wrap
}
.benchmark-title{font-size:13px;font-weight:700;color:#1d1d1f}
.benchmark-copy{font-size:12px;line-height:1.45;color:#6e6e73;margin-top:5px;max-width:680px}
.benchmark-value{
font-size:24px;line-height:1;font-weight:720;letter-spacing:-.035em;color:#1d1d1f
}
.use-benchmark{
border:0;border-radius:999px;background:#0071e3;color:#fff;
padding:8px 12px;font-size:11px;font-weight:650
}
.source-callout{
margin:18px 0 0;padding:16px 17px;
border-radius:18px;background:#f5f5f7;
display:grid;grid-template-columns:1fr auto;gap:12px;align-items:center
}
.source-callout strong{display:block;font-size:13px;margin-bottom:3px}
.source-callout p{margin:0;font-size:11px;line-height:1.45;color:#6e6e73}
.rate-stack{
display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin-top:14px
}
.readonly-value{
font-size:20px;font-weight:720;color:#1d1d1f;padding-top:2px
}
.provenance-grid{
max-width:1100px;margin:44px auto 0;
display:grid;grid-template-columns:repeat(4,1fr);gap:12px
}
.prov-card{
background:#fff;border:1px solid #e5e5e7;border-radius:22px;padding:18px;min-height:165px
}
.prov-card h4{margin:12px 0 6px;font-size:16px;letter-spacing:-.02em}
.prov-card p{margin:0;color:#6e6e73;font-size:11px;line-height:1.45}
.prov-card a{display:inline-block;margin-top:11px;color:#0071e3;font-size:11px;font-weight:650}
.prov-card .status{font-size:10px;color:#8e8e93;margin-top:10px}
@media(max-width:900px){
.provenance-grid{grid-template-columns:1fr 1fr}
.rate-stack{grid-template-columns:1fr}
}
@media(max-width:560px){
.provenance-grid{grid-template-columns:1fr}
.source-callout{grid-template-columns:1fr}
}
@media(max-width:980px){
.story-wrap{grid-template-columns:1fr}
.story-stage{position:relative;top:auto;height:auto;min-height:520px;padding-top:60px}
.story-step{min-height:auto;padding:60px 0}
.story-step-inner{opacity:1;transform:none}
.config-layout{grid-template-columns:1fr}
.summary{position:relative;top:auto}
.econ-grid{grid-template-columns:1fr}
}
@media(max-width:760px){
.primary-nav .inner,.shell{padding-left:15px;padding-right:15px}
.nav-links button:not(.analyze-pill){display:none}
.creator-badge{display:none}
.module{flex-basis:88px}
.hero{padding-top:64px}
.hero h1{font-size:55px}
.hero-copy{font-size:17px}
.house-stage{height:320px;margin-top:32px}
.hf1,.hf2{display:none}
.tile-wide,.tile-mid,.tile-third{grid-column:span 12}
.compare-head,.compare-row{grid-template-columns:1.2fr .9fr .9fr}
.compare-head>div,.compare-row>div{padding:14px 11px}
.compare-value{font-size:14px}
.config-layout{margin-top:28px}
.choice-grid,.field-grid,.metric-grid{grid-template-columns:1fr}
.field.full{grid-column:auto}
.step-pane{padding:22px}
.break-control{grid-template-columns:60px 1fr 60px}
}
@media(prefers-reduced-motion:reduce){
html{scroll-behavior:auto}
.reveal{opacity:1;transform:none}
*{animation:none!important;transition-duration:.01ms!important}
}
</style>
</head>
<body>
<header class="primary-nav">
<div class="inner">
<a class="logo" href="#top">
<span class="logo-mark" title="Slab to Sale">
<svg width="18" height="18" viewBox="0 0 64 64" aria-hidden="true">
<path d="M10 34 32 15 54 34" fill="none" stroke="#0071e3" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17 31v21h30V31" fill="none" stroke="#0071e3" stroke-width="4"/>
</svg>
</span>
Slab to Sale Compare
</a>
<div class="nav-links">
<button data-scroll="#overview">Overview</button>
<button data-scroll="#compare">Compare</button>
<button data-scroll="#economics">Economics</button>
<button data-scroll="#sources">Sources</button>
<button class="analyze-pill" data-scroll="#configure">Analyze</button>
<div class="creator-badge" title="Copyright © 2026 John Darby">
<div class="creator-gem">JD</div>
<div><strong>© 2026</strong><span>John Darby</span></div>
</div>
</div>
</div>
</header>
<nav class="module-nav" aria-label="Slab to Sale modules">
<div class="module-row">
<button class="module" data-scroll="#schedule">
<svg width="28" height="28" viewBox="0 0 32 32" fill="none"><rect x="4" y="6" width="24" height="20" rx="5" stroke="#1d1d1f" stroke-width="1.5"/><path d="M8 11h16M9 16h6M9 20h11" stroke="#0071e3" stroke-width="1.8" stroke-linecap="round"/></svg>
<span class="m-title">Schedule</span><span class="m-sub">Timeline</span>
</button>
<button class="module" data-scroll="#economics">
<svg width="28" height="28" viewBox="0 0 32 32" fill="none"><circle cx="16" cy="16" r="11" stroke="#1d1d1f" stroke-width="1.5"/><path d="M19.5 11.5c-1-1-2.3-1.5-3.8-1.4-2.2.1-3.8 1.2-3.8 2.9 0 4.5 8.4 1.8 8.4 6 0 1.9-1.8 3-4.1 3.1-1.9.1-3.5-.6-4.6-1.8M16 8v16" stroke="#0071e3" stroke-width="1.7" stroke-linecap="round"/></svg>
<span class="m-title">Cost</span><span class="m-sub">Full project</span>
</button>
<button class="module" data-scroll="#financing">
<svg width="28" height="28" viewBox="0 0 32 32" fill="none"><path d="M5 13h22M7 13V25M13 13V25M19 13V25M25 13V25M4 26h24M4 11l12-6 12 6H4z" stroke="#1d1d1f" stroke-width="1.5" stroke-linejoin="round"/><path d="M19 18c0-1.2-1.1-2-2.6-2s-2.5.7-2.5 1.8c0 2.8 5.3 1 5.3 3.6 0 1.2-1.1 1.9-2.7 1.9-1.2 0-2.3-.4-3-1.1" stroke="#0071e3" stroke-width="1.4" stroke-linecap="round"/></svg>
<span class="m-title">Financing</span><span class="m-sub">Interest</span>
</button>
<button class="module" data-scroll="#profit">
<svg width="28" height="28" viewBox="0 0 32 32" fill="none"><path d="M5 24 12 17l5 4 10-12" stroke="#0071e3" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M21 9h6v6" stroke="#1d1d1f" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
<span class="m-title">Profit</span><span class="m-sub">Low / base / high</span>
</button>
<button class="module" data-scroll="#payback">
<svg width="28" height="28" viewBox="0 0 32 32" fill="none"><path d="M5 22h22M8 22V12h16v10M11 12V8h10v4" stroke="#1d1d1f" stroke-width="1.5" stroke-linejoin="round"/><path d="M12 17h8" stroke="#0071e3" stroke-width="2" stroke-linecap="round"/></svg>
<span class="m-title">Payback</span><span class="m-sub">Printer ownership</span>
</button>
<button class="module" data-scroll="#permits">
<svg width="28" height="28" viewBox="0 0 32 32" fill="none"><rect x="7" y="4" width="18" height="24" rx="3" stroke="#1d1d1f" stroke-width="1.5"/><path d="M11 10h10M11 15h10M11 20h6" stroke="#0071e3" stroke-width="1.7" stroke-linecap="round"/></svg>
<span class="m-title">Permits</span><span class="m-sub">Planning inputs</span>
</button>
</div>
</nav>
<main id="top">
<section class="hero shell" id="overview">
<div class="hero-eyebrow">3D construction feasibility</div>
<h1>Will it actually cost less?</h1>
<p class="hero-copy">3D printing can make walls faster. Watch the house build itself, then see whether the <b>whole project</b> becomes faster, cheaper, and more profitable.</p>
<div class="hero-links">
<button class="text-link" data-scroll="#configure">Analyze a project</button>
<button class="text-link" data-scroll="#story">See how it works</button>
</div>
<div class="house-stage">
<div class="motion-house-wrap" aria-label="Animated 3D printer building a house">
<svg class="motion-house-svg" viewBox="0 0 1000 560" role="img" aria-label="3D printed house being built layer by layer">
<defs>
<linearGradient id="mhRoof" x1="0" x2="1">
<stop offset="0" stop-color="#b8bbc1"/>
<stop offset="1" stop-color="#ececef"/>
</linearGradient>
<linearGradient id="mhGlass" x1="0" x2="1">
<stop offset="0" stop-color="#b9dcf4"/>
<stop offset="1" stop-color="#eaf7ff"/>
</linearGradient>
<linearGradient id="mhWall" x1="0" x2="1">
<stop offset="0" stop-color="#f1f1f3"/>
<stop offset=".52" stop-color="#ffffff"/>
<stop offset="1" stop-color="#e6e7ea"/>
</linearGradient>
<filter id="mhShadow">
<feDropShadow dx="0" dy="18" stdDeviation="18" flood-opacity=".13"/>
</filter>
<filter id="mhSoftGlow">
<feGaussianBlur stdDeviation="14"/>
</filter>
</defs>
<ellipse cx="500" cy="492" rx="320" ry="32" fill="#000" opacity=".055"/>
<!-- slab -->
<g class="mh-base" filter="url(#mhShadow)">
<path d="M205 420 480 505 790 414 514 339Z" fill="#d4d6da" stroke="#b8bbc1" stroke-width="2"/>
<path d="M220 410 481 489 770 406" fill="none" stroke="#bfc2c7" stroke-width="4"/>
</g>
<!-- faint wall masses for 3D perspective -->
<g opacity=".22">
<path d="M267 239 497 135 704 226 704 405 491 472 267 404Z" fill="url(#mhWall)" stroke="#bfc5cc" stroke-width="2"/>
<path d="M704 226 786 270 786 378 704 405Z" fill="#d9dde3" stroke="#b8bec5" stroke-width="2"/>
</g>
<!-- front red printed layers -->
<path class="mh-layer mh-l1" d="M273 398 491 465 704 398"/>
<path class="mh-layer mh-l2" d="M273 377 491 444 704 377"/>
<path class="mh-layer mh-l3" d="M273 356 491 423 704 356"/>
<path class="mh-layer mh-l4" d="M273 335 491 402 704 335"/>
<path class="mh-layer mh-l5" d="M273 314 491 381 704 314"/>
<path class="mh-layer mh-l6" d="M273 293 491 360 704 293"/>
<path class="mh-layer mh-l7" d="M273 272 491 339 704 272"/>
<path class="mh-layer mh-l8" d="M273 251 491 318 704 251"/>
<!-- side printed layers -->
<path class="mh-side-layer mh-s1" d="M704 398 783 373"/>
<path class="mh-side-layer mh-s2" d="M704 369 783 344"/>
<path class="mh-side-layer mh-s3" d="M704 340 783 315"/>
<path class="mh-side-layer mh-s4" d="M704 311 783 286"/>
<path class="mh-side-layer mh-s5" d="M704 282 783 257"/>
<!-- printer gantry -->
<g class="mh-gantry">
<path d="M205 168V460M803 168V435M205 180H803" stroke="#62656b" stroke-width="10" stroke-linecap="round"/>
<path d="M244 222H761" stroke="#8d9096" stroke-width="8" stroke-linecap="round"/>
<g class="mh-nozzle">
<rect x="474" y="196" width="54" height="48" rx="8" fill="#2f3237"/>
<path d="M488 244h26l-7 25h-12z" fill="#c81e1e"/>
<path d="M501 269v23" stroke="#c81e1e" stroke-width="6" stroke-linecap="round"/>
<circle cx="501" cy="295" r="4" fill="#c81e1e"/>
</g>
</g>
<!-- roof -->
<g class="mh-roof" filter="url(#mhShadow)">
<path d="M243 244 493 116 735 228 704 255 492 158 273 270Z" fill="url(#mhRoof)" stroke="#aaaeb4" stroke-width="2"/>
<path d="M735 228 805 263 780 284 704 255Z" fill="#aeb2b8" stroke="#999ea4" stroke-width="2"/>
</g>
<!-- door and windows -->
<g class="mh-openings">
<rect x="454" y="344" width="78" height="118" rx="5" fill="url(#mhGlass)" stroke="#8aaac0" stroke-width="2"/>
<rect x="330" y="320" width="72" height="70" rx="5" fill="url(#mhGlass)" stroke="#9ab4c6" stroke-width="2"/>
<rect x="582" y="320" width="72" height="70" rx="5" fill="url(#mhGlass)" stroke="#9ab4c6" stroke-width="2"/>
<rect x="726" y="300" width="38" height="58" rx="4" fill="#c9e5f7" stroke="#9eb8ca" stroke-width="2"/>
</g>
<!-- plumbing / electrical traces -->
<g class="mh-mep" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path d="M313 401v-74h55" stroke="#0071e3" stroke-width="5"/>
<path d="M671 399v-92h-66" stroke="#ff7a00" stroke-width="5"/>
<path d="M494 429v-109" stroke="#16803c" stroke-width="5"/>
<circle cx="368" cy="327" r="6" fill="#0071e3" stroke="none"/>
<circle cx="605" cy="307" r="6" fill="#ff7a00" stroke="none"/>
</g>
<!-- completion glow -->
<ellipse class="mh-glow" cx="505" cy="330" rx="270" ry="170" fill="#70b9ff" opacity=".14" filter="url(#mhSoftGlow)"/>
</svg>
<div class="mh-status">
<span class="mh-status-dot"></span>
<span class="mh-status-text"></span>
</div>
<div class="hero-float hf1">Wall print <b>7 days</b></div>
<div class="hero-float hf2">Whole project <b>192 days</b></div>
</div>
</div>
</section>
<!-- Signature story -->
<section class="story" id="story" data-step="0">
<div class="story-wrap">
<div class="story-stage">
<svg class="story-svg" viewBox="0 0 760 620" role="img" aria-label="House assembly story">
<ellipse cx="380" cy="540" rx="280" ry="27" fill="#fff" opacity=".08"/>
<!-- foundation -->
<path d="M150 458 380 535 615 458 382 390Z" fill="#303036" stroke="#55555c" stroke-width="2"/>
<!-- walls -->
<g class="story-layer wall1">
<path d="M185 275 381 175 576 275v177l-195 67-196-67z" fill="#dedee2" stroke="#77777d" stroke-width="2"/>
<path d="M186 409 381 475 576 409M186 382 381 448 576 382M186 355 381 421 576 355M186 328 381 394 576 328" fill="none" stroke="#6aaee8" stroke-width="6"/>
<rect x="345" y="342" width="72" height="119" fill="#0b0b0c" stroke="#7c7c82"/>
</g>
<g class="story-layer wall2">
<rect x="224" y="331" width="68" height="67" fill="#86bce8" opacity=".9"/>
<rect x="468" y="331" width="68" height="67" fill="#86bce8" opacity=".9"/>
</g>
<g class="story-layer wall3">
<path d="M218 300h326" stroke="#f26c4f" stroke-width="5" stroke-dasharray="11 11"/>
</g>
<!-- roof -->
<g class="story-part roof-part">
<path d="M148 277 381 132 615 277 563 308 381 198 198 308Z" fill="#b9bbc0" stroke="#e5e5e7" stroke-width="2"/>
</g>
<!-- MEP -->
<g class="story-part mep-part">
<path d="M251 419v-79h52M510 422v-97h-51M380 462v-170" fill="none" stroke="#ff8c42" stroke-width="5" stroke-linecap="round"/>
<circle cx="303" cy="340" r="7" fill="#ff8c42"/><circle cx="459" cy="325" r="7" fill="#ff8c42"/>
</g>
<!-- finish -->
<g class="story-part finish-part">
<path d="M185 275 381 175 576 275" fill="none" stroke="#fff" stroke-width="4"/>
<path d="M195 451 381 514 566 451" fill="none" stroke="#79d08c" stroke-width="7"/>
<circle cx="607" cy="224" r="39" fill="#1f8b4c" opacity=".95"/>
<path d="m590 224 12 12 22-27" fill="none" stroke="#fff" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>
</div>
<div class="story-copy">
<article class="story-step active" data-index="0">
<div class="story-step-inner">
<div class="story-kicker">Step 1 · Printed walls</div>
<h3>The printer moves fast.</h3>
<p>Walls may be printed much faster than conventional wall construction. But walls are only one part of the completed home.</p>
<div class="story-metric"><strong>7</strong><span>print days</span></div>
</div>
</article>
<article class="story-step" data-index="1">
<div class="story-step-inner">
<div class="story-kicker">Step 2 · Roof + envelope</div>
<h3>The rest of the house still has to happen.</h3>
<p>Roofing, windows, doors, and weatherproofing remain part of the project regardless of how quickly walls are printed.</p>
<div class="story-metric"><strong>$38K</strong><span>illustrative envelope cost</span></div>
</div>
</article>
<article class="story-step" data-index="2">
<div class="story-step-inner">
<div class="story-kicker">Step 3 · MEP</div>
<h3>Mechanical, electrical, and plumbing become the next constraint.</h3>
<p>After the wall phase shrinks, downstream work can become the schedule bottleneck.</p>
<div class="story-metric"><strong>34</strong><span>illustrative MEP days</span></div>
</div>
</article>
<article class="story-step" data-index="3">
<div class="story-step-inner">
<div class="story-kicker">Step 4 · Whole project</div>
<h3>That's the number that matters.</h3>
<p>Slab to Sale compares the completed project—time, cost, financing, profit, and ownership economics—not the printer in isolation.</p>
<div class="story-metric"><strong>18</strong><span>illustrative days saved</span></div>
</div>
</article>
</div>
</div>
</section>
<!-- Bento highlights -->
<section class="section soft" id="schedule">
<div class="shell reveal">
<div class="section-label">Project highlights</div>
<h2 class="section-title">One project. Several decisions.</h2>
<p class="section-copy">The results that matter most, surfaced before the detailed calculator.</p>
<div class="bento">
<article class="tile tile-wide dark">
<div class="eyebrow">Schedule advantage</div>
<div class="big red" id="bentoDays">18 days</div>
<p>The 3D-print scenario is faster under the current assumptions. The timeline below explains where that advantage comes from.</p>
<svg class="spark" viewBox="0 0 180 80"><path d="M3 67c26-4 29-35 52-33s26 18 49 11 30-40 72-39" fill="none" stroke="#2997ff" stroke-width="4" stroke-linecap="round"/></svg>
</article>
<article class="tile tile-mid blue">
<div class="eyebrow">Loan interest saved</div>
<div class="big bluev" id="bentoInterest">$—</div>
<p>Schedule improvements become dollars by reducing the time an average outstanding construction-loan balance remains open.</p>
</article>
<article class="tile tile-third softtile">
<div class="eyebrow">Break-even wall price</div>
<div class="big" id="bentoBreak">$—</div>
<p>The printed-wall price at which both methods have approximately equal project cost.</p>
</article>
<article class="tile tile-third">
<div class="eyebrow">Base-case project profit</div>
<div class="big greenv" id="bentoProfit">$—</div>
<p id="bentoProfitText">Based on the current base sale value and selected construction method.</p>
</article>
<article class="tile tile-third softtile" id="permits">
<div class="eyebrow">Permit planning</div>
<div class="big bluev" id="permitMetric">14 days</div>
<p>Editable review allowance used as a planning input. This is not a permit approval prediction.</p>
</article>
</div>
</div>
</section>
<!-- Compare models -->
<section class="section" id="compare">
<div class="shell reveal">
<div class="section-label">Compare approaches</div>
<h2 class="section-title">Conventional. 3D printed.</h2>
<p class="section-copy">The same project scope, shown side by side.</p>
<div class="compare-wrap">
<div class="compare-head">
<div></div><div><strong>Conventional</strong></div><div><strong>3D printed</strong></div>
</div>
<div class="compare-row"><div class="compare-label">Estimated duration</div><div class="compare-value" id="cmpConvDays">210 days</div><div class="compare-value print" id="cmpPrintDays">192 days</div></div>
<div class="compare-row"><div class="compare-label">Estimated project cost</div><div class="compare-value" id="cmpConvCost">$—</div><div class="compare-value print" id="cmpPrintCost">$—</div></div>
<div class="compare-row"><div class="compare-label">Construction interest</div><div class="compare-value" id="cmpConvInterest">$—</div><div class="compare-value print" id="cmpPrintInterest">$—</div></div>
<div class="compare-row"><div class="compare-label">Base-case profit</div><div class="compare-value" id="cmpConvProfit">$—</div><div class="compare-value print" id="cmpPrintProfit">$—</div></div>
<div class="compare-row"><div class="compare-label">Primary schedule risk</div><div class="compare-value">Longer wall phase</div><div class="compare-value print" id="cmpRisk">Finishes / downstream</div></div>
</div>
</div>
</section>
<!-- Configurator -->
<section class="section soft" id="configure">
<div class="shell reveal">
<div class="section-label">Project configurator</div>
<h2 class="section-title">Build the comparison step by step.</h2>
<p class="section-copy">Instead of exposing every field at once, Slab to Sale walks through the decisions and keeps a live summary pinned beside them.</p>
<div class="config-layout">
<div class="config-main">
<div class="config-top">
<strong id="stepLabel">Step 1 of 4 · House</strong>
<div class="progress"><span class="on"></span><span></span><span></span><span></span></div>
</div>
<div class="step-pane active" data-step="0">
<h3>Choose the house.</h3>
<p>Start with the basic project shape. These profiles only populate editable planning defaults.</p>
<div class="choice-grid">
<button class="choice selected" data-house="single"><b>Single-family</b><span>1,500 sq ft planning profile</span></button>
<button class="choice" data-house="townhouse"><b>Townhouse</b><span>1,800 sq ft planning profile</span></button>
<button class="choice" data-house="adu"><b>ADU / small home</b><span>800 sq ft planning profile</span></button>
<button class="choice" data-house="twostory"><b>Two-story</b><span>2,400 sq ft planning profile</span></button>
</div>
<div class="field-grid" style="margin-top:18px">
<div class="field full"><label>Project name</label><input id="projectName" value="Austin Demo Home"></div>
<div class="field"><label>Location</label><input id="location" value="Austin, TX"></div>
<div class="field"><label>Home size (sq ft)</label><input id="area" type="number" value="1500" min="100"></div>
</div>
</div>
<div class="step-pane" data-step="1">
<h3>Set the schedule.</h3>
<p>Compare the conventional baseline with the printing, setup, contingency, and downstream phases.</p>
<div class="benchmark-box">
<div class="benchmark-top">
<div>
<div class="benchmark-title">Conventional duration benchmark source</div>
<div class="benchmark-copy">U.S. Census Survey of Construction publishes construction start/completion and housing characteristics that can support a future location/house-type benchmark. This static version keeps the duration editable rather than pretending a live Census benchmark has already been calculated.</div>
<div class="source-row">
<span class="source-badge public">Public · Census SOC</span>
<a class="source-link" href="https://www.census.gov/construction/soc/about.html" target="_blank" rel="noopener">Census website ↗</a>
<span class="source-badge user">Current value · User input</span>
</div>
</div>
</div>
</div>
<div class="field-grid" style="margin-top:14px">
<div class="field">
<label>Conventional total days</label>
<input id="convTotal" type="number" value="210" min="1">
<div class="source-row"><span class="source-badge user">User input</span></div>
</div>
<div class="field"><label>Conventional wall phase</label><input id="convWall" type="number" value="24" min="0"><div class="source-row"><span class="source-badge user">User / contractor input</span></div></div>
<div class="field"><label>3D wall print days</label><input id="printDays" type="number" value="7" min="0"></div>
<div class="field"><label>Setup / mobilization days</label><input id="setupDays" type="number" value="4" min="0"></div>
<div class="field"><label>Print contingency days</label><input id="riskDays" type="number" value="3" min="0"></div>
<div class="field"><label>Parallel-work overlap days</label><input id="overlap" type="number" value="8" min="0"></div>
<div class="field"><label>Foundation days</label><input id="foundation" type="number" value="18" min="0"></div>
<div class="field"><label>Roof days</label><input id="roof" type="number" value="12" min="0"></div>
<div class="field"><label>MEP days</label><input id="mep" type="number" value="34" min="0"></div>
<div class="field"><label>Finishes + closeout days</label><input id="finish" type="number" value="46" min="0"></div>
</div>
</div>
<div class="step-pane" data-step="2">
<h3>Add project economics.</h3>
<p>Keep common project scope in both methods and replace only the wall-system economics. Every value below now shows whether it is a user assumption or has a credible external data source available.</p>
<div class="source-callout">
<div>
<strong>Property value: static version = user input</strong>
<p>RentCast can provide property-specific estimated value plus low/high ranges, but an API key should not be placed inside a public static Space. Live AVM integration belongs behind a backend.</p>
<div class="source-row">
<span class="source-badge live">Commercial API · RentCast</span>
<span class="source-badge user">Current V10 · User input</span>
<a class="source-link" href="https://developers.rentcast.io/reference/value-estimate" target="_blank" rel="noopener">RentCast API ↗</a>
</div>
</div>
</div>
<div class="field-grid">
<div class="field"><label>Low sale value</label><input id="saleLow" type="number" value="430000" min="0"><div class="source-row"><span class="source-badge user">User input</span><span class="source-badge live">RentCast-ready</span></div></div>
<div class="field"><label>Base sale value</label><input id="saleBase" type="number" value="465000" min="0"><div class="source-row"><span class="source-badge user">User input</span><span class="source-badge live">RentCast-ready</span></div></div>
<div class="field"><label>High sale value</label><input id="saleHigh" type="number" value="500000" min="0"><div class="source-row"><span class="source-badge user">User input</span><span class="source-badge live">RentCast-ready</span></div></div>
<div class="field"><label>Selling / closing cost (%)</label><input id="sellingPct" type="number" value="5" min="0"></div>
<div class="field"><label>Land / lot</label><input id="landCost" type="number" value="90000" min="0"></div>
<div class="field"><label>Site + foundation cost</label><input id="siteFoundationCost" type="number" value="48000" min="0"></div>
<div class="field"><label>Roof + windows + doors</label><input id="roofWindowCost" type="number" value="38000" min="0"></div>
<div class="field"><label>MEP cost</label><input id="mepCost" type="number" value="50000" min="0"></div>
<div class="field"><label>Finishes cost</label><input id="finishCost" type="number" value="65000" min="0"></div>
<div class="field"><label>Permits + design</label><input id="permitDesignCost" type="number" value="15000" min="0"></div>
<div class="field"><label>Other common costs</label><input id="otherCommonCost" type="number" value="9000" min="0"></div>
<div class="field"><label>Permit review allowance (days)</label><input id="permitDays" type="number" value="14" min="0"><div class="source-row"><span class="source-badge user">Local jurisdiction / user</span></div></div>
<div class="field"><label>Conventional wall-system cost</label><input id="convWallCost" type="number" value="87000" min="0"><div class="source-row"><span class="source-badge user">Contractor / estimator input</span></div></div>
<div class="field"><label>Printable wall area (wall sq ft)</label><input id="printWallArea" type="number" value="2200" min="1"></div>
<div class="field"><label>3D wall price / wall sq ft</label><input id="printWallPrice" type="number" value="40" min="0" step=".5"><div class="source-row"><span class="source-badge vendor">Vendor / quote input</span></div></div>
<div class="field"><label>Printer mobilization cost</label><input id="printMobilCost" type="number" value="8000" min="0"></div>
<div class="field"><label>3D-specific engineering / permitting</label><input id="printEngineeringCost" type="number" value="4000" min="0"><div class="source-row"><span class="source-badge vendor">Vendor / engineer quote</span></div></div>
</div>
<div class="benchmark-box">
<div class="benchmark-title">Public + licensed sources available for future auto-fill</div>
<div class="benchmark-copy">These websites can strengthen the assumptions without hiding where the numbers came from.</div>
<div class="source-row" style="margin-top:10px">
<span class="source-badge public">FHFA HPI · housing trend</span>
<a class="source-link" href="https://www.fhfa.gov/data/hpi/datasets" target="_blank" rel="noopener">FHFA ↗</a>
<span class="source-badge public">Census BPS · permit activity</span>
<a class="source-link" href="https://www.census.gov/construction/bps/index.html" target="_blank" rel="noopener">Census BPS ↗</a>
<span class="source-badge public">BLS PPI · material trends</span>
<a class="source-link" href="https://www.bls.gov/ppi/" target="_blank" rel="noopener">BLS ↗</a>
<span class="source-badge licensed">RSMeans · local cost data</span>
<a class="source-link" href="https://www.gordian.com/products/rsmeans-data-services/" target="_blank" rel="noopener">Gordian ↗</a>
</div>
</div>
</div>
<div class="step-pane" data-step="3">
<h3>Finish with financing and ownership.</h3>
<p>The financing calculation uses the average outstanding construction-loan balance—not the total project budget. The benchmark rate is separated from the lender spread so the source is visible.</p>
<div class="benchmark-box">
<div class="benchmark-top">
<div>
<div class="benchmark-title">Bank prime benchmark</div>
<div class="benchmark-copy">Static snapshot from FRED / Federal Reserve data. Prime is a benchmark, not your final construction-loan APR.</div>
<div class="source-row">
<span class="source-badge public">Public · FRED</span>
<span class="source-badge estimate">Snapshot · Sep 15, 2026</span>
<a class="source-link" href="https://fred.stlouisfed.org/data/DPRIME" target="_blank" rel="noopener">FRED website ↗</a>
</div>
</div>
<div class="benchmark-value">6.75%</div>
</div>
</div>
<div class="rate-stack">
<div class="field">
<label>Prime benchmark (%)</label>
<input id="primeRate" type="number" value="6.75" min="0" step=".05">
<div class="source-row"><span class="source-badge public">FRED snapshot</span></div>
</div>
<div class="field">
<label>Lender spread (%)</label>
<input id="lenderSpread" type="number" value="2.00" min="0" step=".05">
<div class="source-row"><span class="source-badge user">Lender / user input</span></div>
</div>
<div class="field">
<label>Modeled construction APR</label>
<div class="readonly-value" id="modeledRate">8.75%</div>
<div class="source-row"><span class="source-badge estimate">Calculated</span></div>
</div>
</div>
<div class="field-grid" style="margin-top:14px">
<div class="field">
<label>Average outstanding loan balance</label>
<input id="avgLoanBalance" type="number" value="400000" min="0">
<div class="source-row"><span class="source-badge user">User / lender input</span></div>
</div>
<div class="field">
<label>Printer system purchase price</label>
<input id="printerCapex" type="number" value="899000" min="0">
<div class="source-row"><span class="source-badge vendor">Vendor · ICON</span><a class="source-link" href="https://webflow.iconbuild.com/technology/pricing" target="_blank" rel="noopener">ICON pricing ↗</a></div>
</div>
<div class="field">
<label>Homes completed / year</label>
<input id="homesPerYear" type="number" value="20" min="0">
<div class="source-row"><span class="source-badge user">Builder input</span></div>
</div>
<div class="field">
<label>Annual maintenance</label>
<input id="annualMaintenance" type="number" value="40000" min="0">
<div class="source-row"><span class="source-badge user">Vendor / owner input</span></div>
</div>
<div class="field">
<label>Annual operator / training</label>
<input id="annualOperator" type="number" value="80000" min="0">
<div class="source-row"><span class="source-badge user">Builder input</span><span class="source-badge public">BLS benchmark available</span></div>
</div>
</div>
<div class="benchmark-box">
<div class="benchmark-top">
<div>
<div class="benchmark-title">Operator labor benchmark</div>
<div class="benchmark-copy">BLS reports a U.S. median annual wage of $59,600 for construction equipment operators (May 2025). This is a wage benchmark only—not a fully loaded printer-operator cost or training budget.</div>
<div class="source-row">
<span class="source-badge public">Public · BLS OEWS/OHH</span>
<a class="source-link" href="https://www.bls.gov/ooh/construction-and-extraction/construction-equipment-operators.htm" target="_blank" rel="noopener">BLS website ↗</a>
</div>
</div>
<button class="use-benchmark" type="button" id="useOperatorBenchmark">Use $59,600</button>
</div>
</div>
</div>
<div class="config-actions">
<button class="btn-back" id="backBtn" type="button">Back</button>
<button class="btn-next" id="nextBtn" type="button">Continue</button>
</div>
</div>
<aside class="summary">
<h4>Live project summary</h4>
<div class="summary-project" id="summaryProject">Austin Demo Home · Austin, TX · 1,500 sq ft</div>
<div class="summary-row"><span>Conventional</span><strong id="sumConvDays">210 days</strong></div>
<div class="summary-row"><span>3D scenario</span><strong id="sumPrintDays">192 days</strong></div>
<div class="summary-row"><span>Time saved</span><strong id="sumSaved">18 days</strong></div>
<div class="summary-row"><span>Conventional cost</span><strong id="sumConvCost">$—</strong></div>
<div class="summary-row"><span>3D-print cost</span><strong id="sumPrintCost">$—</strong></div>
<div class="summary-row"><span>Break-even wall price</span><strong id="sumBreak">$—</strong></div>
<div class="summary-verdict">
<div class="vlabel">Current verdict</div>
<div class="vbig" id="sumVerdict">Calculating…</div>
<div class="summary-note" id="sumVerdictNote">The tool can favor either method.</div>
</div>
</aside>
</div>
</div>
</section>
<!-- Economics -->
<section class="section" id="economics">
<div class="shell reveal">
<div class="section-label">Project economics</div>
<h2 class="section-title">Faster doesn't always mean cheaper.</h2>
<p class="section-copy">That's why Slab to Sale keeps the complete project economics visible.</p>
<div class="econ-grid">
<article class="econ-card" id="financing">
<h3>Full-project comparison</h3>
<p>Common construction scope remains in both methods. Only the wall-system economics are substituted.</p>
<div class="metric-grid">
<div class="metric"><span>Conventional total</span><b id="econConvTotal">$—</b></div>
<div class="metric"><span>3D-print total</span><b class="blue" id="econPrintTotal">$—</b></div>
<div class="metric"><span>Loan interest saved</span><b class="red" id="econInterestSaved">$—</b></div>
<div class="metric"><span>Current cost advantage</span><b id="econAdvantage">$—</b></div>
</div>
<div style="margin-top:18px;color:#6e6e73;font-size:13px;line-height:1.5" id="econVerdictText">Calculating…</div>
</article>
<article class="econ-card" id="profit">
<h3>Profit range</h3>
<p>Low, base, and high sale scenarios show how sensitive the project is to market value.</p>
<table class="profit-table">
<thead><tr><th>Scenario</th><th>Sale</th><th>Conventional</th><th>3D print</th></tr></thead>
<tbody>
<tr><td>Low</td><td id="saleLowOut">$—</td><td id="profitConvLow">$—</td><td id="profitPrintLow">$—</td></tr>
<tr><td>Base</td><td id="saleBaseOut">$—</td><td id="profitConvBase">$—</td><td id="profitPrintBase">$—</td></tr>
<tr><td>High</td><td id="saleHighOut">$—</td><td id="profitConvHigh">$—</td><td id="profitPrintHigh">$—</td></tr>
</tbody>
</table>
</article>
</div>
<div class="break-slider">
<div class="break-top">
<div class="section-label">Interactive break-even</div>
<div class="big" id="breakHeadline">$40.00</div>
<p>Printed-wall price per wall sq ft</p>
</div>
<div class="break-control">
<div class="price-box"><b>$10</b>lower</div>
<input id="breakSlider" type="range" min="10" max="70" step=".5" value="40">
<div class="price-box"><b>$70</b>higher</div>
</div>
<div class="break-note" id="breakMessage">Calculating break-even…</div>
</div>
<div class="econ-grid" style="margin-top:16px" id="payback">
<article class="econ-card">
<h3>Printer ownership payback</h3>
<p>Simple payback based on annual project savings after entered maintenance and operator costs.</p>
<div class="metric-grid">
<div class="metric"><span>Printer purchase price</span><b id="payCapex">$899K</b></div>
<div class="metric"><span>Homes / year</span><b class="blue" id="payHomes">20</b></div>
</div>
<div style="margin-top:18px;font-size:24px;font-weight:700;letter-spacing:-.03em" id="paybackTitle">Calculating…</div>
<div style="margin-top:7px;color:#6e6e73;font-size:13px;line-height:1.5" id="paybackText"></div>
</article>
<article class="econ-card">
<h3>Permit planning input</h3>
<p>Permit timing is treated as an explicit assumption rather than silently assumed to be zero.</p>
<div class="metric-grid">
<div class="metric"><span>Review allowance</span><b class="blue" id="permitDaysOut">14 days</b></div>
<div class="metric"><span>3D-specific engineering</span><b id="permitEngOut">$4,000</b></div>
</div>
<div style="margin-top:18px;color:#6e6e73;font-size:13px;line-height:1.5">
This does not predict approval or replace local code review. It makes permitting and special engineering visible in the feasibility model.
</div>
</article>
</div>
</div>
</section>
<section class="section soft" id="sources">
<div class="shell reveal">
<div class="section-label">Data provenance</div>
<h2 class="section-title">Know where every number came from.</h2>
<p class="section-copy">Slab to Sale distinguishes public benchmarks, commercial APIs, vendor pricing, licensed cost data, estimates, and your own project inputs instead of mixing them together.</p>
<div class="provenance-grid">
<article class="prov-card">
<span class="source-badge live">Commercial API</span>
<h4>RentCast</h4>
<p>Potential source for property-level estimated value, low/high value range, and comparable properties.</p>
<a href="https://developers.rentcast.io/reference/value-estimate" target="_blank" rel="noopener">RentCast API ↗</a>
<div class="status">V10 status: not connected — backend required for API key safety.</div>
</article>
<article class="prov-card">
<span class="source-badge public">Public</span>
<h4>FHFA House Price Index</h4>
<p>Housing-price trends by geography. Useful for market context, not a substitute for a property appraisal.</p>
<a href="https://www.fhfa.gov/data/hpi/datasets" target="_blank" rel="noopener">FHFA datasets ↗</a>
<div class="status">V10 status: source linked; live geography lookup not yet connected.</div>
</article>
<article class="prov-card">
<span class="source-badge public">Public</span>
<h4>U.S. Census</h4>
<p>Survey of Construction for housing/build-duration research and Building Permits Survey for local permit activity.</p>
<a href="https://www.census.gov/construction/soc/about.html" target="_blank" rel="noopener">Survey of Construction ↗</a><br>
<a href="https://www.census.gov/construction/bps/index.html" target="_blank" rel="noopener">Building Permits Survey ↗</a>
<div class="status">V10 status: source linked; project duration remains editable.</div>
</article>
<article class="prov-card">
<span class="source-badge public">Public</span>
<h4>BLS</h4>
<p>Producer Price Index for construction-input trends and wage benchmarks for relevant construction occupations.</p>
<a href="https://www.bls.gov/ppi/" target="_blank" rel="noopener">BLS PPI ↗</a><br>
<a href="https://www.bls.gov/ooh/construction-and-extraction/construction-equipment-operators.htm" target="_blank" rel="noopener">Operator wages ↗</a>
<div class="status">V10 status: wage benchmark surfaced; material index not yet auto-applied.</div>
</article>
<article class="prov-card">
<span class="source-badge public">Public</span>
<h4>FRED / Federal Reserve</h4>
<p>Bank prime rate benchmark. Final construction-loan APR still depends on lender spread and project terms.</p>
<a href="https://fred.stlouisfed.org/data/DPRIME" target="_blank" rel="noopener">FRED prime rate ↗</a>
<div class="status">V10 status: 6.75% snapshot + editable lender spread.</div>
</article>
<article class="prov-card">
<span class="source-badge vendor">Vendor</span>
<h4>ICON</h4>
<p>Vendor-published equipment pricing reference for the printer-system purchase-price field.</p>
<a href="https://webflow.iconbuild.com/technology/pricing" target="_blank" rel="noopener">ICON pricing ↗</a>
<div class="status">V10 status: $899K editable vendor benchmark.</div>
</article>
<article class="prov-card">
<span class="source-badge licensed">Licensed</span>
<h4>RSMeans / Gordian</h4>
<p>Commercial localized material, labor, and equipment cost database for a future production-grade cost engine.</p>
<a href="https://www.gordian.com/products/rsmeans-data-services/" target="_blank" rel="noopener">RSMeans Data ↗</a>
<div class="status">V10 status: not connected — licensed access required.</div>
</article>
<article class="prov-card">
<span class="source-badge user">Project-specific</span>
<h4>Builder / lender / contractor</h4>
<p>Quotes, draw schedules, average outstanding loan balance, annual utilization, maintenance, and project-specific permit assumptions.</p>
<div class="status">V10 status: editable user inputs remain authoritative for these fields.</div>
</article>
</div>
</div>
</section>
<section class="footnotes">
<strong style="color:#1d1d1f">Notes and assumptions</strong>
<ol>
<li>All default project values are illustrative planning assumptions. Replace them with project-specific contractor, vendor, lender, and market inputs.</li>
<li>The 3D-print scenario replaces the wall-system scope only. Land, site work, foundation, roof, windows, doors, MEP, finishes, permits, and other common project costs remain separately modeled.</li>
<li>Construction-loan interest is simplified as average outstanding balance × APR × duration / 365. Actual lender draws, fees, compounding, and loan structures may differ.</li>
<li>The printer purchase-price field uses an editable vendor benchmark and links to ICON pricing. Verify current equipment, pump, material, training, shipping, maintenance, and service pricing directly with the vendor before commercial use.</li>
<li>Permit timing is an editable planning allowance, not a prediction of approval, code compliance, or jurisdiction-specific review time.</li>
<li>Source badges identify whether a value is public data, vendor data, licensed/commercial data, an estimate, or user input. A linked source does not mean the static V10 page has fetched that source live.</li>
</ol>
<div class="footer">
<div class="footer-inner">
<span>© 2026 John Darby · Slab to Sale</span>
<span>Feasibility model · Not a bid, appraisal, engineering opinion, or lending recommendation.</span>
</div>
</div>
</section>
</main>
<script>
const profiles={
single:{label:"Single-family",area:1500,convTotal:210,convWall:24,printDays:7,setupDays:4,riskDays:3,overlap:8,foundation:18,roof:12,mep:34,finish:46},
townhouse:{label:"Townhouse",area:1800,convTotal:195,convWall:22,printDays:7,setupDays:4,riskDays:3,overlap:9,foundation:16,roof:10,mep:32,finish:42},
adu:{label:"ADU",area:800,convTotal:145,convWall:16,printDays:4,setupDays:3,riskDays:2,overlap:6,foundation:12,roof:8,mep:24,finish:30},
twostory:{label:"Two-story",area:2400,convTotal:270,convWall:34,printDays:11,setupDays:5,riskDays:5,overlap:10,foundation:22,roof:15,mep:45,finish:58}
};
let activeHouse="single";
let currentStep=0;
function el(id){return document.getElementById(id)}
function n(id){const x=Number(el(id)?.value);return Number.isFinite(x)?Math.max(0,x):0}
function money(v){
const abs=Math.abs(v);
const s=new Intl.NumberFormat("en-US",{style:"currency",currency:"USD",maximumFractionDigits:0}).format(abs);
return v<0?"-"+s:s
}
function price(v){
return new Intl.NumberFormat("en-US",{style:"currency",currency:"USD",minimumFractionDigits:2,maximumFractionDigits:2}).format(v)
}
function profitClass(node,v){
node.classList.remove("positive","negative");
node.classList.add(v>=0?"positive":"negative")
}
function schedule(){
const conv=n("convTotal"), convWall=n("convWall");
const total3d=Math.max(0,conv-convWall+n("printDays")+n("setupDays")+n("riskDays")-n("overlap"));
const saved=conv-total3d;
return {conv,total3d,saved,pct:conv?saved/conv*100:0}
}
function economics(){
const s=schedule();
const modeledRate=n("primeRate")+n("lenderSpread");
setText("modeledRate",`${modeledRate.toFixed(2)}%`);
const balance=n("avgLoanBalance"), apr=modeledRate/100;
const convInterest=balance*apr*(s.conv/365);
const printInterest=balance*apr*(s.total3d/365);
const interestSaved=convInterest-printInterest;
const common=n("landCost")+n("siteFoundationCost")+n("roofWindowCost")+n("mepCost")+n("finishCost")+n("permitDesignCost")+n("otherCommonCost");
const convTotal=common+n("convWallCost")+convInterest;
const area=Math.max(1,n("printWallArea"));
const printWall=area*n("printWallPrice")+n("printMobilCost")+n("printEngineeringCost");
const printTotal=common+printWall+printInterest;
const advantage=convTotal-printTotal;
const breakEven=Math.max(0,(n("convWallCost")+interestSaved-n("printMobilCost")-n("printEngineeringCost"))/area);
const close=Math.min(100,n("sellingPct"))/100;
const baseNet=n("saleBase")*(1-close);
return { ...s, convInterest, printInterest, interestSaved, convTotal, printTotal, advantage, breakEven,
baseConvProfit:baseNet-convTotal, basePrintProfit:baseNet-printTotal };
}
function setText(id,value){if(el(id))el(id).textContent=value}
function updateAll(){
const s=schedule(), e=economics();
const project=`${el("projectName").value||"Untitled Project"} · ${el("location").value||"Location not entered"} · ${Math.round(n("area")).toLocaleString()} sq ft`;
setText("summaryProject",project);
setText("sumConvDays",`${Math.round(s.conv)} days`);
setText("sumPrintDays",`${Math.round(s.total3d)} days`);
setText("sumSaved",`${Math.round(s.saved)} days`);
setText("sumConvCost",money(e.convTotal));
setText("sumPrintCost",money(e.printTotal));
setText("sumBreak",price(e.breakEven));
setText("bentoDays",`${Math.round(s.saved)} days`);
setText("bentoInterest",money(e.interestSaved));
setText("bentoBreak",price(e.breakEven));
const bestProfit=e.advantage>0?e.basePrintProfit:e.baseConvProfit;
setText("bentoProfit",money(bestProfit));
setText("bentoProfitText",e.advantage>0?"Base-case 3D-print profit under current assumptions.":"Base-case conventional profit under current assumptions.");
setText("cmpConvDays",`${Math.round(s.conv)} days`);
setText("cmpPrintDays",`${Math.round(s.total3d)} days`);
setText("cmpConvCost",money(e.convTotal));
setText("cmpPrintCost",money(e.printTotal));
setText("cmpConvInterest",money(e.convInterest));
setText("cmpPrintInterest",money(e.printInterest));
setText("cmpConvProfit",money(e.baseConvProfit));
setText("cmpPrintProfit",money(e.basePrintProfit));
const phases=[
["Foundation",n("foundation")],["Roof",n("roof")],["MEP",n("mep")],["Finishes",n("finish")],
["3D wall print",n("printDays")],["Setup",n("setupDays")],["Contingency",n("riskDays")]
].sort((a,b)=>b[1]-a[1]);
setText("cmpRisk",`${phases[0][0]} (${Math.round(phases[0][1])} days)`);
setText("econConvTotal",money(e.convTotal));
setText("econPrintTotal",money(e.printTotal));
setText("econInterestSaved",money(e.interestSaved));
setText("econAdvantage",money(Math.abs(e.advantage)));
if(e.advantage>1){
setText("sumVerdict",`3D lower by ${money(e.advantage)}`);
el("sumVerdict").className="vbig green";
setText("sumVerdictNote","Under the current project, financing, and wall-system assumptions.");
setText("econVerdictText",`3D printing is lower cost by ${money(e.advantage)} under the entered assumptions. Base-case profit is ${money(e.basePrintProfit)} with 3D printing versus ${money(e.baseConvProfit)} conventionally.`);
}else if(e.advantage<-1){
setText("sumVerdict",`Conventional lower by ${money(Math.abs(e.advantage))}`);
el("sumVerdict").className="vbig red";
setText("sumVerdictNote","The time advantage does not offset the current printed-wall economics.");
setText("econVerdictText",`Conventional construction is lower cost by ${money(Math.abs(e.advantage))}. Base-case profit is ${money(e.baseConvProfit)} conventionally versus ${money(e.basePrintProfit)} with 3D printing.`);
}else{
setText("sumVerdict","Near cost parity");
el("sumVerdict").className="vbig";
setText("sumVerdictNote","Break-even pricing and execution risk become especially important.");
setText("econVerdictText","The two methods are approximately equal in cost under the current assumptions.");
}
[["Low","saleLow","saleLowOut","profitConvLow","profitPrintLow"],["Base","saleBase","saleBaseOut","profitConvBase","profitPrintBase"],["High","saleHigh","saleHighOut","profitConvHigh","profitPrintHigh"]]
.forEach(row=>{
const sale=n(row[1]), net=sale*(1-Math.min(100,n("sellingPct"))/100);
setText(row[2],money(sale));
setText(row[3],money(net-e.convTotal));
setText(row[4],money(net-e.printTotal));
profitClass(el(row[3]),net-e.convTotal);
profitClass(el(row[4]),net-e.printTotal);
});
setText("permitMetric",`${Math.round(n("permitDays"))} days`);
setText("permitDaysOut",`${Math.round(n("permitDays"))} days`);
setText("permitEngOut",money(n("printEngineeringCost")));
const capex=n("printerCapex"), homes=n("homesPerYear");
const own=n("annualMaintenance")+n("annualOperator");
const projectSavings=Math.max(0,e.advantage);
const netAnnual=projectSavings*homes-own;
setText("payCapex",money(capex));
setText("payHomes",String(Math.round(homes)));
if(e.advantage<=0){
setText("paybackTitle","No payback from project savings yet");
setText("paybackText","The 3D-print scenario is not cheaper per project under the current assumptions, so printer ownership is not justified from project savings alone.");
}else if(netAnnual<=0){
setText("paybackTitle","Annual savings do not cover ownership costs");
setText("paybackText",`Gross project savings are ${money(projectSavings*homes)} per year versus ${money(own)} of entered maintenance and operator costs.`);
}else{
setText("paybackTitle",`${(capex/netAnnual).toFixed(1)} years simple payback`);
setText("paybackText",`Estimated annual net savings are ${money(netAnnual)} after the entered maintenance and operator costs. Financing, taxes, depreciation, downtime, and resale value are excluded.`);
}
const slider=el("breakSlider");
if(document.activeElement!==slider) slider.value=n("printWallPrice");
setText("breakHeadline",price(n("printWallPrice")));
const delta=n("printWallPrice")-e.breakEven;
if(Math.abs(delta)<.01) setText("breakMessage",`At approximately ${price(e.breakEven)} per wall sq ft, the two methods are at project-cost break-even.`);
else if(delta<0) setText("breakMessage",`Your current wall price is ${price(Math.abs(delta))} below the calculated break-even point of ${price(e.breakEven)}. Under these assumptions, that supports the 3D-print cost case.`);
else setText("breakMessage",`Your current wall price is ${price(delta)} above the calculated break-even point of ${price(e.breakEven)}. Under these assumptions, conventional construction keeps the cost advantage.`);
}
function applyProfile(type){
activeHouse=type;
document.querySelectorAll(".choice[data-house]").forEach(x=>x.classList.toggle("selected",x.dataset.house===type));
const p=profiles[type];
["area","convTotal","convWall","printDays","setupDays","riskDays","overlap","foundation","roof","mep","finish"].forEach(k=>el(k).value=p[k]);
updateAll();
}
document.querySelectorAll(".choice[data-house]").forEach(btn=>btn.addEventListener("click",()=>applyProfile(btn.dataset.house)));
function showStep(i){
currentStep=Math.max(0,Math.min(3,i));
document.querySelectorAll(".step-pane").forEach((p,idx)=>p.classList.toggle("active",idx===currentStep));
document.querySelectorAll(".progress span").forEach((p,idx)=>p.classList.toggle("on",idx<=currentStep));
setText("stepLabel",`Step ${currentStep+1} of 4 · ${["House","Schedule","Economics","Financing"][currentStep]}`);
el("backBtn").style.visibility=currentStep===0?"hidden":"visible";
setText("nextBtn",currentStep===3?"View results":"Continue");
}
el("backBtn").addEventListener("click",()=>showStep(currentStep-1));
el("nextBtn").addEventListener("click",()=>{
if(currentStep<3)showStep(currentStep+1);
else document.querySelector("#economics").scrollIntoView({behavior:"smooth"});
});
document.querySelectorAll("[data-scroll]").forEach(btn=>{
btn.addEventListener("click",()=>{
const target=document.querySelector(btn.getAttribute("data-scroll"));
if(target)target.scrollIntoView({behavior:"smooth",block:"start"});
});
});
document.querySelectorAll("#configure input").forEach(inp=>inp.addEventListener("input",updateAll));
el("breakSlider").addEventListener("input",e=>{
el("printWallPrice").value=e.target.value;
updateAll();
});
el("useOperatorBenchmark").addEventListener("click",()=>{
el("annualOperator").value=59600;
updateAll();
});
const steps=[...document.querySelectorAll(".story-step")];
const story=document.querySelector(".story");
if("IntersectionObserver" in window){
const storyIO=new IntersectionObserver(entries=>{
entries.forEach(entry=>{
if(entry.isIntersecting){
const idx=Number(entry.target.dataset.index);
story.dataset.step=idx;
steps.forEach(s=>s.classList.toggle("active",s===entry.target));
}
});
},{rootMargin:"-30% 0px -45% 0px",threshold:.1});
steps.forEach(s=>storyIO.observe(s));
const revealIO=new IntersectionObserver(entries=>{
entries.forEach(e=>{if(e.isIntersecting)e.target.classList.add("show")});
},{threshold:.12});
document.querySelectorAll(".reveal").forEach(x=>revealIO.observe(x));
}else{
document.querySelectorAll(".reveal").forEach(x=>x.classList.add("show"));
}
showStep(0);
updateAll();
</script>
</body>
</html>