Download index.html from CodeAntidote/printhome-time-optimizer: direct link, hf CLI and curl.
- Browser
- Download file 77.4 kB
-
https://huggingface.co/spaces/CodeAntidote/printhome-time-optimizer/resolve/main/index.html
- Command line
-
hf download hf://spaces/CodeAntidote/printhome-time-optimizer/index.html
-
curl -L -o index.html https://huggingface.co/spaces/CodeAntidote/printhome-time-optimizer/resolve/main/index.html
77.4 kB
| <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> | |