D2C — Coffee / Beverage Landing
A warm, craft-forward direct-to-consumer landing page for a fictional specialty coffee brand. Built with semantic HTML, kraft-paper and espresso styling, and vanilla JavaScript, it pairs an origin-story hero and product-bag mockup with an interactive roast-level selector, flavor-note grid, a four-step pour-over guide, a subscribe-versus-one-time pricing toggle, verified reviews, a sustainability section, an accordion FAQ, a sticky cart, and a footer.
MCP
Code
/* ===========================================================
Driftwood Roasters — D2C coffee landing
Kraft paper + espresso brown + cream · craft/artisanal
=========================================================== */
:root {
--kraft: #c9a87a;
--kraft-deep: #b08d5c;
--espresso: #2c1d12;
--espresso-2: #3e2b1c;
--bean: #5a3a22;
--cream: #f4ece0;
--cream-2: #ece0cd;
--paper: #efe4d2;
--ink: #28190f;
--muted: #6e5840;
--line: #d8c6a8;
--rust: #a8472a;
--gold: #c98a36;
--leaf: #6f7b4a;
--white: #fffaf2;
--radius: 18px;
--radius-s: 11px;
--shadow: 0 18px 50px -22px rgba(44, 29, 18, .55);
--shadow-s: 0 8px 24px -12px rgba(44, 29, 18, .4);
--ease: cubic-bezier(.22, .61, .36, 1);
--serif: "Bitter", Georgia, "Times New Roman", serif;
--sans: "Work Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--mono: "JetBrains Mono", ui-monospace, "SF Mono", monospace;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
body {
font-family: var(--sans);
color: var(--ink);
line-height: 1.5;
background: var(--cream);
background-image:
radial-gradient(1100px 600px at 80% -8%, rgba(201, 168, 122, .35), transparent 60%),
radial-gradient(900px 500px at -5% 8%, rgba(168, 71, 42, .08), transparent 55%);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow-x: hidden;
}
img { max-width: 100%; display: block; }
.wrap { width: min(1160px, 92vw); margin-inline: auto; }
.skip-link {
position: absolute; left: -999px; top: 0; z-index: 200;
background: var(--espresso); color: var(--cream);
padding: .6rem 1rem; border-radius: 0 0 8px 0;
}
.skip-link:focus { left: 0; }
/* ---------- shared type ---------- */
.eyebrow {
display: inline-block;
font-family: var(--mono);
font-size: .72rem; letter-spacing: .18em; text-transform: uppercase;
color: var(--gold);
padding: .35rem .7rem;
border: 1px solid rgba(201, 138, 54, .4);
border-radius: 100px;
margin-bottom: 1.1rem;
}
.eyebrow--dark { color: var(--rust); border-color: rgba(168, 71, 42, .35); }
.section { padding: clamp(4rem, 9vw, 7.5rem) 0; }
.section__title {
font-family: var(--serif);
font-weight: 800;
font-size: clamp(1.9rem, 4.4vw, 3rem);
line-height: 1.08;
letter-spacing: -.01em;
color: var(--espresso);
}
.section__title em { color: var(--rust); font-style: italic; }
.section__lede {
margin-top: 1rem;
color: var(--muted);
font-size: clamp(1rem, 1.6vw, 1.12rem);
max-width: 56ch;
}
/* ---------- buttons ---------- */
.btn {
--pad: .8rem 1.35rem;
display: inline-flex; align-items: center; justify-content: center;
gap: .5rem;
font-family: var(--sans);
font-weight: 600; font-size: .98rem;
padding: var(--pad);
border-radius: 100px;
border: 1.5px solid transparent;
cursor: pointer;
text-decoration: none;
transition: transform .25s var(--ease), box-shadow .25s var(--ease),
background .2s, color .2s, border-color .2s;
}
.btn--lg { --pad: 1rem 1.7rem; font-size: 1.04rem; }
.btn--solid {
background: var(--espresso); color: var(--cream);
box-shadow: 0 12px 26px -14px rgba(44, 29, 18, .9);
}
.btn--solid:hover { background: var(--bean); transform: translateY(-3px); box-shadow: 0 18px 34px -16px rgba(44, 29, 18, .85); }
.btn--solid:active { transform: translateY(-1px); }
.btn--ghost {
background: transparent; color: var(--espresso);
border-color: var(--bean);
}
.btn--ghost:hover { background: var(--espresso); color: var(--cream); transform: translateY(-3px); }
.btn:focus-visible { outline: 3px solid var(--gold); outline-offset: 3px; }
/* ---------- nav ---------- */
.nav {
position: sticky; top: 0; z-index: 100;
backdrop-filter: blur(10px);
background: rgba(244, 236, 224, .72);
border-bottom: 1px solid transparent;
transition: background .3s, border-color .3s, box-shadow .3s;
}
.nav.is-stuck {
background: rgba(244, 236, 224, .92);
border-color: var(--line);
box-shadow: var(--shadow-s);
}
.nav__inner { display: flex; align-items: center; gap: 1.5rem; height: 72px; }
.brand { display: flex; align-items: center; gap: .55rem; text-decoration: none; color: var(--espresso); }
.brand__mark { color: var(--rust); display: grid; place-items: center; }
.brand__name { font-family: var(--serif); font-weight: 800; font-size: 1.22rem; letter-spacing: -.01em; }
.brand__sub { color: var(--kraft-deep); margin-left: .25rem; font-weight: 600; }
.nav__links { display: flex; gap: 1.6rem; margin-inline: auto; }
.nav__links a {
color: var(--muted); text-decoration: none; font-weight: 500; font-size: .95rem;
position: relative; padding: .3rem 0;
}
.nav__links a::after {
content: ""; position: absolute; left: 0; bottom: 0; height: 2px; width: 0;
background: var(--rust); transition: width .25s var(--ease);
}
.nav__links a:hover { color: var(--espresso); }
.nav__links a:hover::after { width: 100%; }
.nav__actions { display: flex; align-items: center; gap: .9rem; }
.cart-btn {
position: relative; background: none; border: none; cursor: pointer;
color: var(--espresso); display: grid; place-items: center; padding: .3rem;
}
.cart-btn:hover { color: var(--rust); }
.cart-count {
position: absolute; top: -6px; right: -8px;
background: var(--rust); color: #fff;
font-size: .68rem; font-weight: 700;
min-width: 18px; height: 18px; padding: 0 4px;
border-radius: 100px; display: grid; place-items: center;
transform: scale(0); transition: transform .25s var(--ease);
}
.cart-count.is-on { transform: scale(1); }
.nav__toggle {
display: none; flex-direction: column; gap: 5px;
background: none; border: none; cursor: pointer; padding: 6px;
}
.nav__toggle span { width: 24px; height: 2px; background: var(--espresso); border-radius: 2px; transition: transform .3s, opacity .3s; }
.nav__toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav__toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav__toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
.mobile-menu {
display: flex; flex-direction: column; gap: .25rem;
padding: 1rem 4vw 1.4rem;
background: var(--cream);
border-bottom: 1px solid var(--line);
}
.mobile-menu a { padding: .85rem .25rem; text-decoration: none; color: var(--espresso); font-weight: 600; border-bottom: 1px solid var(--line); }
.mobile-menu .btn { margin-top: .8rem; justify-content: center; }
/* ---------- hero ---------- */
.hero { padding: clamp(3rem, 7vw, 6rem) 0 clamp(4rem, 8vw, 6rem); }
.hero__inner {
display: grid; grid-template-columns: 1.05fr .95fr; gap: clamp(2rem, 5vw, 4rem);
align-items: center;
}
.hero__title {
font-family: var(--serif); font-weight: 800;
font-size: clamp(2.6rem, 6.6vw, 4.6rem);
line-height: 1.02; letter-spacing: -.02em;
color: var(--espresso);
}
.hero__title em { color: var(--rust); font-style: italic; }
.hero__lede { margin: 1.4rem 0 2rem; color: var(--muted); font-size: clamp(1.02rem, 1.7vw, 1.18rem); max-width: 48ch; }
.hero__cta { display: flex; gap: .9rem; flex-wrap: wrap; }
.hero__trust {
display: flex; gap: 1.6rem; flex-wrap: wrap; list-style: none;
margin-top: 2.4rem; padding-top: 1.6rem; border-top: 1px dashed var(--line);
}
.hero__trust li { font-size: .9rem; color: var(--muted); }
.hero__trust strong { display: block; font-family: var(--serif); font-size: 1.15rem; color: var(--espresso); }
/* the coffee bag mockup */
.hero__visual { position: relative; display: grid; place-items: center; min-height: 440px; }
.bag {
position: relative; width: clamp(220px, 30vw, 300px); aspect-ratio: 3 / 4.3;
border-radius: 6px 6px 14px 14px;
background:
linear-gradient(160deg, var(--kraft) 0%, var(--kraft-deep) 70%, #9c794d 100%);
box-shadow: var(--shadow), inset 0 2px 0 rgba(255, 255, 255, .25), inset -16px 0 30px -20px rgba(0,0,0,.4);
transform: rotate(-4deg);
animation: bagFloat 7s var(--ease) infinite;
overflow: hidden;
}
.bag::after { /* paper grain */
content: ""; position: absolute; inset: 0;
background-image: repeating-linear-gradient(90deg, rgba(255,255,255,.04) 0 2px, transparent 2px 5px);
mix-blend-mode: overlay;
}
@keyframes bagFloat { 0%,100% { transform: rotate(-4deg) translateY(0); } 50% { transform: rotate(-4deg) translateY(-12px); } }
.bag__top {
position: absolute; top: 0; left: 0; right: 0; height: 26px;
background: var(--espresso);
clip-path: polygon(0 0,100% 0,100% 60%,92% 100%,84% 60%,76% 100%,68% 60%,60% 100%,52% 60%,44% 100%,36% 60%,28% 100%,20% 60%,12% 100%,8% 60%,0 100%);
}
.bag__body { position: relative; height: 100%; padding: 3.2rem 1.4rem 1.4rem; display: flex; flex-direction: column; color: var(--espresso); text-align: center; }
.bag__kicker { font-family: var(--mono); font-size: .64rem; letter-spacing: .22em; text-transform: uppercase; color: var(--bean); }
.bag__name { font-family: var(--serif); font-weight: 800; font-size: clamp(1.5rem, 3vw, 2rem); margin-top: .35rem; line-height: 1.05; }
.bag__notes { font-size: .8rem; color: var(--espresso-2); margin-top: .5rem; font-style: italic; }
.bag__seal {
width: 64px; height: 64px; border-radius: 50%;
display: grid; place-items: center; margin: auto auto .8rem;
background: var(--rust); color: var(--cream);
font-family: var(--serif); font-weight: 800; font-size: 1.3rem;
box-shadow: 0 6px 16px -6px rgba(0,0,0,.5), inset 0 0 0 3px rgba(255,255,255,.25);
}
.bag__weight { font-family: var(--mono); font-size: .68rem; letter-spacing: .12em; color: var(--bean); }
.bag__steam { position: absolute; top: 4%; left: 50%; transform: translateX(-50%); display: flex; gap: 14px; z-index: 0; }
.bag__steam span {
display: block; width: 5px; height: 60px; border-radius: 100px;
background: linear-gradient(to top, rgba(255,255,255,.55), transparent);
filter: blur(2px); opacity: 0; animation: steam 3.4s ease-in-out infinite;
}
.bag__steam span:nth-child(2) { animation-delay: .9s; height: 80px; }
.bag__steam span:nth-child(3) { animation-delay: 1.7s; }
@keyframes steam { 0% { opacity: 0; transform: translateY(10px) scaleY(.6); } 40% { opacity: .8; } 100% { opacity: 0; transform: translateY(-30px) scaleY(1.2); } }
.hero__chip {
position: absolute; background: var(--white); border: 1px solid var(--line);
border-radius: 100px; padding: .5rem .9rem; font-size: .82rem; font-weight: 600;
color: var(--espresso); box-shadow: var(--shadow-s);
}
.hero__chip--a { top: 14%; left: -2%; animation: chip 6s var(--ease) infinite; }
.hero__chip--b { bottom: 12%; right: -4%; animation: chip 6s var(--ease) infinite 1.5s; }
.hero__chip::before { content: "●"; color: var(--leaf); margin-right: .4rem; }
.hero__chip--b::before { color: var(--rust); }
@keyframes chip { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-9px); } }
/* ---------- social proof ---------- */
.proof { padding: 2.2rem 0; border-block: 1px solid var(--line); background: rgba(236, 224, 205, .5); }
.proof__label { text-align: center; font-family: var(--mono); font-size: .7rem; letter-spacing: .2em; text-transform: uppercase; color: var(--muted); margin-bottom: 1.1rem; }
.proof__logos { display: flex; flex-wrap: wrap; justify-content: center; gap: clamp(1.4rem, 5vw, 3.2rem); }
.proof__logos span { font-family: var(--serif); font-weight: 600; font-size: clamp(1rem, 2vw, 1.35rem); color: var(--bean); opacity: .65; transition: opacity .25s, transform .25s; }
.proof__logos span:hover { opacity: 1; transform: translateY(-2px); }
/* ---------- roast selector ---------- */
.roast { background: var(--espresso); color: var(--cream); position: relative; overflow: hidden; }
.roast::before { content: ""; position: absolute; inset: 0; background: radial-gradient(700px 400px at 90% 0, rgba(201,138,54,.18), transparent 60%); pointer-events: none; }
.roast__inner { position: relative; display: grid; grid-template-columns: .85fr 1.15fr; gap: clamp(2rem, 5vw, 4rem); align-items: center; }
.roast .section__title { color: var(--cream); }
.roast .section__lede { color: rgba(244, 236, 224, .72); }
.roast .eyebrow--dark { color: var(--gold); border-color: rgba(201,138,54,.4); }
.roast__panel { display: grid; grid-template-columns: auto 1fr; gap: clamp(1.5rem, 4vw, 2.6rem); align-items: center; background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.1); border-radius: var(--radius); padding: clamp(1.5rem, 4vw, 2.5rem); }
.roast__bean {
width: clamp(110px, 18vw, 160px); aspect-ratio: 1; border-radius: 50% 50% 48% 52% / 52% 50% 50% 48%;
position: relative; transition: background .5s var(--ease), box-shadow .5s var(--ease);
box-shadow: inset -10px -10px 24px rgba(0,0,0,.5), 0 14px 30px -14px rgba(0,0,0,.6);
}
.roast__bean::after { content: ""; position: absolute; top: 8%; left: 50%; width: 8%; height: 84%; background: rgba(0,0,0,.35); border-radius: 100px; transform: translateX(-50%) rotate(8deg); }
.roast__level { font-family: var(--serif); font-weight: 800; font-size: clamp(1.6rem, 3.4vw, 2.2rem); color: var(--gold); display: block; }
.roast__desc { color: rgba(244,236,224,.85); margin: .6rem 0 1.3rem; min-height: 3.4em; }
.roast__meta { display: grid; gap: .7rem; margin-bottom: 1.5rem; }
.roast__meta > div { display: grid; grid-template-columns: 80px 1fr; align-items: center; gap: .8rem; font-size: .82rem; color: rgba(244,236,224,.7); }
.meter { height: 7px; background: rgba(255,255,255,.12); border-radius: 100px; overflow: hidden; }
.meter i { display: block; height: 100%; width: 30%; background: linear-gradient(90deg, var(--gold), var(--rust)); border-radius: 100px; transition: width .5s var(--ease); }
.roast__slider { -webkit-appearance: none; appearance: none; width: 100%; height: 6px; border-radius: 100px; background: linear-gradient(90deg, var(--kraft), var(--bean), var(--espresso-2)); outline: none; margin-bottom: .6rem; }
.roast__slider::-webkit-slider-thumb { -webkit-appearance: none; width: 24px; height: 24px; border-radius: 50%; background: var(--gold); border: 3px solid var(--espresso); cursor: pointer; box-shadow: 0 4px 10px -3px rgba(0,0,0,.6); transition: transform .15s; }
.roast__slider::-webkit-slider-thumb:hover { transform: scale(1.12); }
.roast__slider::-moz-range-thumb { width: 22px; height: 22px; border-radius: 50%; background: var(--gold); border: 3px solid var(--espresso); cursor: pointer; }
.roast__slider:focus-visible { outline: 2px solid var(--gold); outline-offset: 6px; }
.roast__ticks { display: flex; justify-content: space-between; font-family: var(--mono); font-size: .66rem; letter-spacing: .04em; color: rgba(244,236,224,.55); margin-bottom: 1.4rem; }
/* ---------- flavor ---------- */
.flavor__head { text-align: center; display: grid; justify-items: center; margin-bottom: 3rem; }
.flavor__head .section__lede { text-align: center; }
.flavor__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.3rem; }
.note {
background: var(--white); border: 1px solid var(--line); border-radius: var(--radius);
padding: 1.8rem 1.5rem; box-shadow: var(--shadow-s);
transition: transform .3s var(--ease), box-shadow .3s var(--ease), border-color .3s;
}
.note:hover { transform: translateY(-6px); box-shadow: var(--shadow); border-color: var(--kraft-deep); }
.note__emoji { font-size: 2rem; display: block; margin-bottom: .8rem; }
.note h3 { font-family: var(--serif); font-weight: 700; font-size: 1.2rem; color: var(--espresso); margin-bottom: .5rem; }
.note p { color: var(--muted); font-size: .94rem; }
/* ---------- brew ---------- */
.brew { background: rgba(236, 224, 205, .55); }
.brew__intro { text-align: center; display: grid; justify-items: center; margin-bottom: 3.2rem; }
.brew__intro .section__lede { text-align: center; }
.brew__steps { list-style: none; display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.3rem; counter-reset: step; }
.brew__step {
position: relative; background: var(--white); border: 1px solid var(--line);
border-radius: var(--radius); padding: 2rem 1.5rem 1.5rem;
transition: transform .3s var(--ease), box-shadow .3s var(--ease);
}
.brew__step:hover { transform: translateY(-5px); box-shadow: var(--shadow); }
.brew__num { font-family: var(--mono); font-size: .8rem; letter-spacing: .1em; color: var(--rust); font-weight: 500; }
.brew__step h3 { font-family: var(--serif); font-weight: 700; font-size: 1.25rem; color: var(--espresso); margin: .5rem 0 .6rem; }
.brew__step p { color: var(--muted); font-size: .92rem; }
.brew__time { display: inline-block; margin-top: 1rem; font-family: var(--mono); font-size: .72rem; color: var(--bean); background: var(--cream-2); padding: .25rem .6rem; border-radius: 100px; }
/* ---------- pricing ---------- */
.pricing__head { text-align: center; display: grid; justify-items: center; margin-bottom: 3rem; }
.pricing__head .section__lede { text-align: center; }
.toggle { display: inline-flex; gap: 4px; margin-top: 1.6rem; background: var(--cream-2); padding: 5px; border-radius: 100px; border: 1px solid var(--line); }
.toggle__opt {
border: none; cursor: pointer; background: transparent; color: var(--muted);
font-family: var(--sans); font-weight: 600; font-size: .92rem;
padding: .6rem 1.2rem; border-radius: 100px;
display: inline-flex; align-items: center; gap: .5rem;
transition: background .25s, color .25s;
}
.toggle__opt.is-active { background: var(--espresso); color: var(--cream); box-shadow: var(--shadow-s); }
.toggle__save { font-family: var(--mono); font-size: .64rem; background: var(--gold); color: var(--espresso); padding: .12rem .45rem; border-radius: 100px; }
.pricing__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; align-items: stretch; }
.plan {
position: relative; background: var(--white); border: 1px solid var(--line);
border-radius: var(--radius); padding: 2.2rem 1.9rem;
display: flex; flex-direction: column;
transition: transform .3s var(--ease), box-shadow .3s var(--ease);
}
.plan:hover { transform: translateY(-6px); box-shadow: var(--shadow); }
.plan--feature { background: var(--espresso); color: var(--cream); border-color: var(--espresso); transform: scale(1.03); }
.plan--feature:hover { transform: scale(1.03) translateY(-6px); }
.plan__badge { position: absolute; top: -13px; left: 50%; transform: translateX(-50%); background: var(--gold); color: var(--espresso); font-family: var(--mono); font-size: .68rem; letter-spacing: .1em; text-transform: uppercase; padding: .35rem .8rem; border-radius: 100px; }
.plan__name { font-family: var(--serif); font-weight: 800; font-size: 1.5rem; margin-bottom: .4rem; }
.plan--feature .plan__name { color: var(--cream); }
.plan__blurb { color: var(--muted); font-size: .92rem; min-height: 2.8em; }
.plan--feature .plan__blurb { color: rgba(244,236,224,.78); }
.plan__price { margin: 1.2rem 0; display: flex; align-items: baseline; gap: .4rem; }
.plan__amt { font-family: var(--serif); font-weight: 800; font-size: 2.6rem; color: var(--espresso); }
.plan--feature .plan__amt { color: var(--gold); }
.plan__per { color: var(--muted); font-size: .9rem; }
.plan--feature .plan__per { color: rgba(244,236,224,.7); }
.plan__list { list-style: none; display: grid; gap: .65rem; margin-bottom: 1.7rem; flex: 1; }
.plan__list li { position: relative; padding-left: 1.6rem; font-size: .93rem; color: var(--ink); }
.plan--feature .plan__list li { color: rgba(244,236,224,.9); }
.plan__list li::before { content: ""; position: absolute; left: 0; top: .35rem; width: 14px; height: 8px; border-left: 2px solid var(--rust); border-bottom: 2px solid var(--rust); transform: rotate(-45deg); }
.plan--feature .plan__list li::before { border-color: var(--gold); }
.plan .btn { width: 100%; }
/* ---------- reviews ---------- */
.reviews__head { text-align: center; display: grid; justify-items: center; margin-bottom: 2.8rem; }
.reviews__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.4rem; }
.review { background: var(--white); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.9rem 1.7rem; box-shadow: var(--shadow-s); transition: transform .3s var(--ease); }
.review:hover { transform: translateY(-5px); }
.stars { color: var(--gold); letter-spacing: .15em; font-size: 1.05rem; }
.review blockquote { font-family: var(--serif); font-size: 1.08rem; line-height: 1.5; color: var(--espresso); margin: .9rem 0 1.3rem; }
.review figcaption { display: flex; align-items: center; gap: .6rem; font-weight: 600; font-size: .9rem; color: var(--espresso); }
.review figcaption span:last-child { color: var(--muted); font-weight: 400; font-size: .82rem; }
.review__avatar { display: grid; place-items: center; width: 38px; height: 38px; border-radius: 50%; background: var(--kraft); color: var(--espresso); font-family: var(--serif); font-weight: 800; font-size: .85rem; }
/* ---------- sustainability ---------- */
.sustain { background: var(--espresso-2); color: var(--cream); position: relative; overflow: hidden; }
.sustain__inner { display: grid; grid-template-columns: 1.15fr .85fr; gap: clamp(2rem, 5vw, 4rem); align-items: center; }
.sustain .section__title { color: var(--cream); }
.sustain .section__lede { color: rgba(244,236,224,.78); }
.sustain .eyebrow { color: var(--gold); border-color: rgba(201,138,54,.4); }
.sustain__stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.2rem; margin: 2rem 0; }
.sustain__stats > div { padding-left: 1rem; border-left: 2px solid var(--leaf); }
.sustain__stats strong { display: block; font-family: var(--serif); font-weight: 800; font-size: 1.9rem; color: var(--gold); }
.sustain__stats span { font-size: .82rem; color: rgba(244,236,224,.72); }
.sustain__art { position: relative; min-height: 280px; display: grid; place-items: center; }
.sustain__badge {
width: 130px; height: 130px; border-radius: 50%; display: grid; place-items: center; text-align: center;
background: var(--leaf); color: var(--cream); font-family: var(--serif); font-weight: 800; font-size: 1.2rem; line-height: 1.1;
box-shadow: 0 14px 36px -14px rgba(0,0,0,.6); z-index: 2; transform: rotate(-8deg);
}
.leaf { position: absolute; width: 90px; height: 90px; background: var(--leaf); opacity: .5; border-radius: 0 50% 0 50%; }
.leaf--1 { top: 18%; left: 22%; transform: rotate(30deg); animation: float 8s ease-in-out infinite; }
.leaf--2 { bottom: 16%; right: 20%; transform: rotate(-20deg); background: var(--kraft); opacity: .35; animation: float 8s ease-in-out infinite 2s; }
.leaf--3 { top: 50%; right: 10%; width: 60px; height: 60px; transform: rotate(60deg); opacity: .4; animation: float 8s ease-in-out infinite 4s; }
@keyframes float { 0%,100% { transform: translateY(0) rotate(20deg); } 50% { transform: translateY(-16px) rotate(20deg); } }
/* ---------- faq ---------- */
.faq__inner { display: grid; grid-template-columns: .8fr 1.2fr; gap: clamp(2rem, 5vw, 4rem); align-items: start; }
.faq__list { display: grid; gap: .8rem; }
.acc { background: var(--white); border: 1px solid var(--line); border-radius: var(--radius-s); overflow: hidden; transition: box-shadow .25s, border-color .25s; }
.acc.is-open { box-shadow: var(--shadow-s); border-color: var(--kraft-deep); }
.acc__q {
width: 100%; text-align: left; background: none; border: none; cursor: pointer;
font-family: var(--serif); font-weight: 700; font-size: 1.08rem; color: var(--espresso);
padding: 1.2rem 1.4rem; display: flex; align-items: center; justify-content: space-between; gap: 1rem;
}
.acc__q:hover { color: var(--rust); }
.acc__icon { position: relative; flex: none; width: 16px; height: 16px; }
.acc__icon::before, .acc__icon::after { content: ""; position: absolute; background: var(--rust); border-radius: 2px; transition: transform .3s var(--ease); }
.acc__icon::before { top: 7px; left: 0; width: 16px; height: 2px; }
.acc__icon::after { top: 0; left: 7px; width: 2px; height: 16px; }
.acc.is-open .acc__icon::after { transform: rotate(90deg); opacity: 0; }
.acc__a { max-height: 0; overflow: hidden; transition: max-height .35s var(--ease); }
.acc__a p { padding: 0 1.4rem 1.3rem; color: var(--muted); font-size: .96rem; }
/* ---------- final cta ---------- */
.finalcta { text-align: center; }
.finalcta__inner {
background: linear-gradient(150deg, var(--kraft) 0%, var(--kraft-deep) 100%);
border-radius: 28px; padding: clamp(3rem, 7vw, 5rem) 1.5rem;
box-shadow: var(--shadow); position: relative; overflow: hidden;
}
.finalcta__inner::before { content: ""; position: absolute; inset: 0; background: repeating-linear-gradient(90deg, rgba(255,255,255,.05) 0 2px, transparent 2px 6px); }
.finalcta h2 { position: relative; font-family: var(--serif); font-weight: 800; font-size: clamp(1.8rem, 4.5vw, 2.9rem); color: var(--espresso); }
.finalcta p { position: relative; color: var(--espresso-2); margin: 1rem auto 2rem; max-width: 40ch; }
.finalcta .btn { position: relative; }
/* ---------- footer ---------- */
.footer { background: var(--espresso); color: rgba(244,236,224,.8); padding: 3.5rem 0 2rem; }
.footer__inner { display: grid; grid-template-columns: 1.2fr 2fr; gap: 2.5rem; padding-bottom: 2.5rem; border-bottom: 1px solid rgba(255,255,255,.1); }
.footer__brand .brand__name { color: var(--cream); }
.footer__brand p { margin-top: .8rem; font-size: .92rem; max-width: 34ch; }
.footer__cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; }
.footer__cols h4 { font-family: var(--mono); font-size: .72rem; letter-spacing: .14em; text-transform: uppercase; color: var(--gold); margin-bottom: 1rem; }
.footer__cols a { display: block; color: rgba(244,236,224,.78); text-decoration: none; font-size: .92rem; padding: .3rem 0; transition: color .2s, transform .2s; }
.footer__cols a:hover { color: var(--cream); transform: translateX(3px); }
.footer__bottom { display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap; padding-top: 1.5rem; font-size: .82rem; color: rgba(244,236,224,.55); }
/* ---------- sticky cart ---------- */
.sticky-cart {
position: fixed; left: 50%; bottom: 1.2rem; transform: translateX(-50%) translateY(150%);
z-index: 120; display: flex; align-items: center; gap: 1.4rem;
background: var(--white); border: 1px solid var(--line); border-radius: 100px;
padding: .65rem .7rem .65rem 1.4rem; box-shadow: var(--shadow);
transition: transform .4s var(--ease); width: min(440px, 92vw);
}
.sticky-cart.is-on { transform: translateX(-50%) translateY(0); }
.sticky-cart__info { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.sticky-cart__count { font-weight: 700; font-size: .9rem; color: var(--espresso); }
.sticky-cart__last { font-size: .78rem; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sticky-cart .btn { white-space: nowrap; }
/* ---------- toast ---------- */
.toast {
position: fixed; bottom: 5.5rem; left: 50%; transform: translateX(-50%) translateY(20px);
z-index: 130; background: var(--espresso); color: var(--cream);
padding: .85rem 1.3rem; border-radius: 100px; font-size: .92rem; font-weight: 500;
box-shadow: var(--shadow); opacity: 0; pointer-events: none;
transition: opacity .3s, transform .3s var(--ease);
}
.toast.is-on { opacity: 1; transform: translateX(-50%) translateY(0); }
/* ---------- reveal ---------- */
.reveal { opacity: 0; transform: translateY(26px); transition: opacity .7s var(--ease), transform .7s var(--ease); }
.reveal.is-in { opacity: 1; transform: none; }
/* ===========================================================
Responsive
=========================================================== */
@media (max-width: 980px) {
.hero__inner, .roast__inner, .sustain__inner, .faq__inner { grid-template-columns: 1fr; }
.roast__panel { grid-template-columns: 1fr; text-align: center; justify-items: center; }
.roast__meta > div { justify-self: stretch; }
.flavor__grid, .brew__steps { grid-template-columns: repeat(2, 1fr); }
.pricing__grid, .reviews__grid { grid-template-columns: 1fr; max-width: 460px; margin-inline: auto; }
.plan--feature { transform: none; }
.plan--feature:hover { transform: translateY(-6px); }
.hero__visual { order: -1; min-height: 360px; }
}
@media (max-width: 720px) {
.nav__links, .nav__cta { display: none; }
.nav__toggle { display: flex; }
.footer__inner { grid-template-columns: 1fr; }
.sustain__stats { grid-template-columns: 1fr; }
.footer__bottom { flex-direction: column; }
}
@media (max-width: 520px) {
.flavor__grid, .brew__steps { grid-template-columns: 1fr; }
.footer__cols { grid-template-columns: 1fr 1fr; }
.hero__trust { gap: 1.1rem; }
.sticky-cart { padding-left: 1.1rem; gap: .8rem; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
html { scroll-behavior: auto; }
.reveal { opacity: 1; transform: none; }
}/* ===========================================================
Driftwood Roasters — landing interactions (vanilla JS)
=========================================================== */
(function () {
"use strict";
/* ---------- tiny helpers ---------- */
var $ = function (s, c) { return (c || document).querySelector(s); };
var $$ = function (s, c) { return Array.prototype.slice.call((c || document).querySelectorAll(s)); };
var toastEl = $("#toast");
var toastTimer;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("is-on");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () { toastEl.classList.remove("is-on"); }, 2400);
}
/* ---------- sticky nav shadow ---------- */
var nav = $(".nav");
function onScroll() {
if (nav) nav.classList.toggle("is-stuck", window.scrollY > 12);
}
window.addEventListener("scroll", onScroll, { passive: true });
onScroll();
/* ---------- mobile menu ---------- */
var toggle = $("#navToggle");
var menu = $("#mobileMenu");
function closeMenu() {
if (!menu) return;
menu.hidden = true;
if (toggle) { toggle.setAttribute("aria-expanded", "false"); toggle.setAttribute("aria-label", "Open menu"); }
}
if (toggle && menu) {
toggle.addEventListener("click", function () {
var open = toggle.getAttribute("aria-expanded") === "true";
menu.hidden = open;
toggle.setAttribute("aria-expanded", String(!open));
toggle.setAttribute("aria-label", open ? "Open menu" : "Close menu");
});
$$("a", menu).forEach(function (a) { a.addEventListener("click", closeMenu); });
}
/* ---------- smooth scroll (with nav offset) ---------- */
$$('a[href^="#"]').forEach(function (link) {
link.addEventListener("click", function (e) {
var id = link.getAttribute("href");
if (id === "#" || id.length < 2) return;
var target = document.querySelector(id);
if (!target) return;
e.preventDefault();
var top = target.getBoundingClientRect().top + window.scrollY - 72;
window.scrollTo({ top: top, behavior: "smooth" });
});
});
/* ---------- scroll reveal ---------- */
var reveals = $$(".reveal");
if ("IntersectionObserver" in window) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (en) {
if (en.isIntersecting) { en.target.classList.add("is-in"); io.unobserve(en.target); }
});
}, { threshold: 0.14, rootMargin: "0px 0px -40px 0px" });
reveals.forEach(function (el, i) {
el.style.transitionDelay = (i % 4) * 70 + "ms";
io.observe(el);
});
} else {
reveals.forEach(function (el) { el.classList.add("is-in"); });
}
/* ---------- roast selector ---------- */
var ROASTS = [
{ level: "Light", price: 19,
desc: "Stopped just after first crack. Tea-like body, vivid acidity, and a wash of citrus blossom on the finish.",
hex: "#7c5230", body: 28, acid: 92, sweet: 55 },
{ level: "Medium", price: 19,
desc: "Balanced and rounded. Caramelized sugars meet a soft stone-fruit acidity — the everyday crowd-pleaser.",
hex: "#5e3a1f", body: 58, acid: 64, sweet: 80 },
{ level: "Med-Dark", price: 20,
desc: "Developed deeper into second crack. Cocoa, toasted hazelnut and a syrupy weight built for milk drinks.",
hex: "#43271459", body: 80, acid: 38, sweet: 70 },
{ level: "Dark", price: 20,
desc: "Bold and molten. Dark chocolate, smoke and a bittersweet finish — espresso that stands its ground.",
hex: "#2c1810", body: 95, acid: 20, sweet: 48 }
];
var slider = $("#roastSlider");
var bean = $("#roastBean");
var levelEl = $("#roastLevel");
var descEl = $("#roastDesc");
var addBtn = $("#roastAdd");
var mBody = $("#meterBody"), mAcid = $("#meterAcid"), mSweet = $("#meterSweet");
function renderRoast(i) {
var r = ROASTS[i];
if (!r) return;
if (bean) bean.style.background = r.hex;
if (levelEl) levelEl.textContent = r.level;
if (descEl) descEl.textContent = r.desc;
if (mBody) mBody.style.width = r.body + "%";
if (mAcid) mAcid.style.width = r.acid + "%";
if (mSweet) mSweet.style.width = r.sweet + "%";
if (addBtn) {
addBtn.textContent = "Add this roast — $" + r.price;
addBtn.setAttribute("data-cart-add", "Yirgacheffe — " + r.level);
addBtn.setAttribute("data-price", r.price);
}
}
if (slider) {
slider.addEventListener("input", function () { renderRoast(parseInt(slider.value, 10)); });
renderRoast(0);
}
/* ---------- pricing toggle ---------- */
var optOnce = $("#optOnce");
var optSub = $("#optSub");
var amounts = $$(".plan__amt");
function setMode(sub) {
amounts.forEach(function (el) {
var v = sub ? el.getAttribute("data-sub") : el.getAttribute("data-once");
if (v) el.textContent = "$" + v;
});
if (optOnce) { optOnce.classList.toggle("is-active", !sub); optOnce.setAttribute("aria-pressed", String(!sub)); }
if (optSub) { optSub.classList.toggle("is-active", sub); optSub.setAttribute("aria-pressed", String(sub)); }
}
if (optOnce) optOnce.addEventListener("click", function () { setMode(false); });
if (optSub) optSub.addEventListener("click", function () { setMode(true); toast("Subscribers save 20% — nice."); });
/* ---------- FAQ accordion ---------- */
$$(".acc").forEach(function (acc) {
var q = $(".acc__q", acc);
var a = $(".acc__a", acc);
if (!q || !a) return;
q.addEventListener("click", function () {
var open = acc.classList.contains("is-open");
$$(".acc").forEach(function (other) {
if (other !== acc) {
other.classList.remove("is-open");
var oa = $(".acc__a", other), oq = $(".acc__q", other);
if (oa) oa.style.maxHeight = null;
if (oq) oq.setAttribute("aria-expanded", "false");
}
});
acc.classList.toggle("is-open", !open);
q.setAttribute("aria-expanded", String(!open));
a.style.maxHeight = open ? null : a.scrollHeight + "px";
});
});
/* ---------- cart ---------- */
var cart = [];
var navCount = $("#navCartCount");
var navCountWrap = $(".cart-count");
var stickyCart = $("#stickyCart");
var stickyCount = $("#stickyCount");
var stickyLast = $("#stickyLast");
var stickyTotal = $("#stickyTotal");
function priceFor(btn, name) {
var p = btn && btn.getAttribute("data-price");
if (p) return parseInt(p, 10);
// infer from copy/known products
var map = { "The Daily": 19, "Explorer": 36, "Roastery": 64, "Sampler": 24, "Yirgacheffe": 19 };
for (var key in map) { if (name.indexOf(key) > -1) return map[key]; }
return 19;
}
function renderCart() {
var count = cart.length;
var total = cart.reduce(function (s, it) { return s + it.price; }, 0);
if (navCount) navCount.textContent = String(count);
if (navCountWrap) navCountWrap.classList.toggle("is-on", count > 0);
if (stickyCount) stickyCount.textContent = count + (count === 1 ? " item" : " items");
if (stickyTotal) stickyTotal.textContent = "$" + total;
if (stickyLast && cart.length) stickyLast.textContent = cart[cart.length - 1].name;
if (stickyCart) {
stickyCart.hidden = count === 0;
requestAnimationFrame(function () { stickyCart.classList.toggle("is-on", count > 0); });
}
}
function addToCart(name, btn) {
var price = priceFor(btn, name);
cart.push({ name: name, price: price });
renderCart();
toast("Added “" + name + "” to cart");
}
document.addEventListener("click", function (e) {
var btn = e.target.closest("[data-cart-add]");
if (!btn) return;
addToCart(btn.getAttribute("data-cart-add"), btn);
});
var cartBtn = $("#cartBtn");
if (cartBtn) cartBtn.addEventListener("click", function () {
if (!cart.length) { toast("Your cart is empty — pick a roast above."); return; }
toast(cart.length + " bag" + (cart.length > 1 ? "s" : "") + " ready · $" + cart.reduce(function (s, i) { return s + i.price; }, 0));
});
var checkoutBtn = $("#checkoutBtn");
if (checkoutBtn) checkoutBtn.addEventListener("click", function () {
toast("Demo only — no real checkout. Thanks for browsing!");
});
/* ---------- year (if any) ---------- */
// keep footer static; nothing else needed.
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Driftwood Roasters — Small-Batch Specialty Coffee, Shipped Fresh</title>
<meta name="description" content="Single-origin, small-batch coffee roasted to order and shipped within 48 hours. Pick your roast, dial in your flavor, subscribe and save." />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Bitter:ital,wght@0,400;0,600;0,800;1,400&family=Work+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@500&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<!-- NAV -->
<header class="nav" id="top">
<div class="wrap nav__inner">
<a class="brand" href="#top" aria-label="Driftwood Roasters home">
<span class="brand__mark" aria-hidden="true">
<svg viewBox="0 0 24 24" width="26" height="26" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 13h13a3 3 0 0 1 0 6H8a5 5 0 0 1-5-5z"/>
<path d="M16 13h2a3 3 0 0 1 0 6h-1"/>
<path d="M7 5c0 1.5 1 1.5 1 3M11 5c0 1.5 1 1.5 1 3"/>
</svg>
</span>
<span class="brand__name">Driftwood<span class="brand__sub">Roasters</span></span>
</a>
<nav class="nav__links" aria-label="Primary">
<a href="#roast">Roasts</a>
<a href="#flavor">Flavor</a>
<a href="#brew">Brewing</a>
<a href="#pricing">Subscribe</a>
<a href="#faq">FAQ</a>
</nav>
<div class="nav__actions">
<button class="cart-btn" id="cartBtn" aria-label="Open cart, 0 items">
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M3 4h2l2.4 12.4a2 2 0 0 0 2 1.6h7.7a2 2 0 0 0 2-1.6L21 8H6"/><circle cx="9" cy="20" r="1"/><circle cx="18" cy="20" r="1"/></svg>
<span class="cart-count" id="navCartCount">0</span>
</button>
<a href="#pricing" class="btn btn--solid nav__cta">Shop coffee</a>
<button class="nav__toggle" id="navToggle" aria-expanded="false" aria-controls="mobileMenu" aria-label="Open menu">
<span></span><span></span><span></span>
</button>
</div>
</div>
<div class="mobile-menu" id="mobileMenu" hidden>
<a href="#roast">Roasts</a>
<a href="#flavor">Flavor</a>
<a href="#brew">Brewing</a>
<a href="#pricing">Subscribe</a>
<a href="#faq">FAQ</a>
<a href="#pricing" class="btn btn--solid">Shop coffee</a>
</div>
</header>
<main id="main">
<!-- HERO -->
<section class="hero">
<div class="wrap hero__inner">
<div class="hero__copy reveal">
<span class="eyebrow">Roasted to order · Shipped in 48h</span>
<h1 class="hero__title">Coffee that still<br/><em>remembers the farm.</em></h1>
<p class="hero__lede">Driftwood Roasters works direct with twelve smallholder farms across Ethiopia, Colombia and Sumatra. We roast in five-kilo batches the morning your order lands — then send it straight to your door, never a warehouse shelf.</p>
<div class="hero__cta">
<a href="#pricing" class="btn btn--solid btn--lg" data-cart-add="Driftwood Sampler">Start your first bag</a>
<a href="#brew" class="btn btn--ghost btn--lg">See the brew guide</a>
</div>
<ul class="hero__trust">
<li><strong>4.9/5</strong> · 2,800+ reviews</li>
<li><strong>100%</strong> traceable lots</li>
<li><strong>Carbon-neutral</strong> shipping</li>
</ul>
</div>
<div class="hero__visual reveal">
<div class="bag">
<div class="bag__top"></div>
<div class="bag__body">
<span class="bag__kicker">Single Origin</span>
<span class="bag__name">Yirgacheffe</span>
<span class="bag__notes">Jasmine · Bergamot · Honey</span>
<div class="bag__seal">DR</div>
<span class="bag__weight">340g · whole bean</span>
</div>
</div>
<div class="bag__steam" aria-hidden="true"><span></span><span></span><span></span></div>
<div class="hero__chip hero__chip--a">Direct trade</div>
<div class="hero__chip hero__chip--b">Roasted today</div>
</div>
</div>
</section>
<!-- SOCIAL PROOF -->
<section class="proof">
<div class="wrap">
<p class="proof__label">As poured in</p>
<div class="proof__logos">
<span>The Daily Grind</span>
<span>Brewline</span>
<span>Cupping Co.</span>
<span>Roast & Co</span>
<span>Pour Over Weekly</span>
</div>
</div>
</section>
<!-- ROAST SELECTOR -->
<section class="section roast" id="roast">
<div class="wrap roast__inner">
<div class="roast__intro reveal">
<span class="eyebrow eyebrow--dark">Find your roast</span>
<h2 class="section__title">From bright & floral to deep & molten.</h2>
<p class="section__lede">Slide through the roast spectrum to see how the same Ethiopian green bean transforms in the drum.</p>
</div>
<div class="roast__panel reveal">
<div class="roast__bean" id="roastBean" aria-hidden="true"></div>
<div class="roast__detail">
<span class="roast__level" id="roastLevel">Light</span>
<p class="roast__desc" id="roastDesc">Stopped just after first crack. Tea-like body, vivid acidity, and a wash of citrus blossom on the finish.</p>
<div class="roast__meta">
<div><span>Body</span><div class="meter"><i id="meterBody"></i></div></div>
<div><span>Acidity</span><div class="meter"><i id="meterAcid"></i></div></div>
<div><span>Sweetness</span><div class="meter"><i id="meterSweet"></i></div></div>
</div>
<input type="range" min="0" max="3" step="1" value="0" id="roastSlider" class="roast__slider" aria-label="Roast level" />
<div class="roast__ticks" aria-hidden="true">
<span>Light</span><span>Medium</span><span>Med-Dark</span><span>Dark</span>
</div>
<button class="btn btn--solid" id="roastAdd" data-cart-add="Yirgacheffe — Light">Add this roast — $19</button>
</div>
</div>
</div>
</section>
<!-- FLAVOR NOTES -->
<section class="section flavor" id="flavor">
<div class="wrap">
<div class="flavor__head reveal">
<span class="eyebrow eyebrow--dark">The cup</span>
<h2 class="section__title">Tasting notes, not marketing words.</h2>
<p class="section__lede">Every lot is scored on a SCA cupping form before it ships. Here's what our Q-graders actually wrote down.</p>
</div>
<div class="flavor__grid">
<article class="note reveal">
<span class="note__emoji" aria-hidden="true">🍑</span>
<h3>Stone Fruit</h3>
<p>Ripe apricot and white peach lead the Colombian lots — juicy, round, and lingering.</p>
</article>
<article class="note reveal">
<span class="note__emoji" aria-hidden="true">🌸</span>
<h3>Floral</h3>
<p>Jasmine and bergamot define the washed Yirgacheffe. Pour it and the room smells like spring.</p>
</article>
<article class="note reveal">
<span class="note__emoji" aria-hidden="true">🍫</span>
<h3>Cocoa & Spice</h3>
<p>The wet-hulled Sumatra brings dark chocolate, cedar and a hint of clove. Built for espresso.</p>
</article>
<article class="note reveal">
<span class="note__emoji" aria-hidden="true">🍯</span>
<h3>Caramel Sweetness</h3>
<p>Slow drying develops a honeyed, brown-sugar base that holds up against milk.</p>
</article>
</div>
</div>
</section>
<!-- BREW GUIDE -->
<section class="section brew" id="brew">
<div class="wrap brew__inner">
<div class="brew__intro reveal">
<span class="eyebrow">Pour-over 101</span>
<h2 class="section__title">A great cup in four steps.</h2>
<p class="section__lede">No barista certification required. This is the exact recipe we use at the roastery counter.</p>
</div>
<ol class="brew__steps">
<li class="brew__step reveal">
<span class="brew__num">01</span>
<h3>Grind & weigh</h3>
<p>22g of beans, medium-fine, like coarse sand. Heat water to 96°C.</p>
<span class="brew__time">~1 min</span>
</li>
<li class="brew__step reveal">
<span class="brew__num">02</span>
<h3>Bloom</h3>
<p>Pour 50g water, let the grounds breathe and degas for 30 seconds.</p>
<span class="brew__time">0:30</span>
</li>
<li class="brew__step reveal">
<span class="brew__num">03</span>
<h3>Pour in stages</h3>
<p>Add water in slow spirals up to 360g total, keeping the bed level.</p>
<span class="brew__time">2:00</span>
</li>
<li class="brew__step reveal">
<span class="brew__num">04</span>
<h3>Draw down & sip</h3>
<p>Drain by 3:30. Swirl, wait 60 seconds, and taste it cool slightly.</p>
<span class="brew__time">3:30</span>
</li>
</ol>
</div>
</section>
<!-- PRICING -->
<section class="section pricing" id="pricing">
<div class="wrap">
<div class="pricing__head reveal">
<span class="eyebrow eyebrow--dark">Two ways to drink</span>
<h2 class="section__title">One-time order, or never run out.</h2>
<p class="section__lede">Subscribers save 20%, skip or cancel anytime, and get first access to micro-lots.</p>
<div class="toggle" role="group" aria-label="Billing type">
<button class="toggle__opt is-active" id="optOnce" aria-pressed="true">One-time</button>
<button class="toggle__opt" id="optSub" aria-pressed="false">Subscribe <span class="toggle__save">Save 20%</span></button>
</div>
</div>
<div class="pricing__grid">
<article class="plan reveal">
<h3 class="plan__name">The Daily</h3>
<p class="plan__blurb">One bag, your pick of roast. The easy way in.</p>
<p class="plan__price"><span class="plan__amt" data-once="19" data-sub="15">$19</span><span class="plan__per">/ 340g bag</span></p>
<ul class="plan__list">
<li>One single-origin bag</li>
<li>Roasted to order</li>
<li>Free shipping over $35</li>
</ul>
<button class="btn btn--ghost" data-cart-add="The Daily — 340g">Add to cart</button>
</article>
<article class="plan plan--feature reveal">
<span class="plan__badge">Most loved</span>
<h3 class="plan__name">The Explorer</h3>
<p class="plan__blurb">Two rotating origins, chosen by our head roaster each month.</p>
<p class="plan__price"><span class="plan__amt" data-once="36" data-sub="29">$36</span><span class="plan__per">/ 2 bags</span></p>
<ul class="plan__list">
<li>Two curated origins</li>
<li>Tasting card + brew notes</li>
<li>Priority on micro-lots</li>
<li>Free carbon-neutral shipping</li>
</ul>
<button class="btn btn--solid" data-cart-add="The Explorer — 2 bags">Add to cart</button>
</article>
<article class="plan reveal">
<h3 class="plan__name">The Roastery</h3>
<p class="plan__blurb">Four bags for households that go through coffee fast.</p>
<p class="plan__price"><span class="plan__amt" data-once="64" data-sub="51">$64</span><span class="plan__per">/ 4 bags</span></p>
<ul class="plan__list">
<li>Four bags, mix & match</li>
<li>Grind to order</li>
<li>Members-only cupping invites</li>
</ul>
<button class="btn btn--ghost" data-cart-add="The Roastery — 4 bags">Add to cart</button>
</article>
</div>
</div>
</section>
<!-- REVIEWS -->
<section class="section reviews">
<div class="wrap">
<div class="reviews__head reveal">
<span class="eyebrow">Word of mouth</span>
<h2 class="section__title">2,800 ratings. 4.9 stars.</h2>
</div>
<div class="reviews__grid">
<figure class="review reveal">
<div class="stars" aria-label="5 out of 5 stars">★★★★★</div>
<blockquote>The Yirgacheffe genuinely smells like flowers. I cancelled my supermarket habit the day it arrived and never looked back.</blockquote>
<figcaption><span class="review__avatar">MA</span> Mara A. · <span>verified subscriber</span></figcaption>
</figure>
<figure class="review reveal">
<div class="stars" aria-label="5 out of 5 stars">★★★★★</div>
<blockquote>You can taste that it was roasted days, not months, ago. The Sumatra pulls a ridiculous espresso shot.</blockquote>
<figcaption><span class="review__avatar">DK</span> Devon K. · <span>verified buyer</span></figcaption>
</figure>
<figure class="review reveal">
<div class="stars" aria-label="5 out of 5 stars">★★★★★</div>
<blockquote>Love that every bag tells me the farm and the roast date. It feels like coffee with a paper trail.</blockquote>
<figcaption><span class="review__avatar">JL</span> Jules L. · <span>verified subscriber</span></figcaption>
</figure>
</div>
</div>
</section>
<!-- SUSTAINABILITY -->
<section class="section sustain">
<div class="wrap sustain__inner">
<div class="sustain__copy reveal">
<span class="eyebrow">Beyond the cup</span>
<h2 class="section__title">Paid fair. Roasted clean. Shipped light.</h2>
<p class="section__lede">We publish what we pay growers — on average 2.4× the commodity price — and reinvest 1% of revenue into the watersheds our farms depend on.</p>
<div class="sustain__stats">
<div><strong>2.4×</strong><span>above commodity price paid to farmers</span></div>
<div><strong>100%</strong><span>compostable bags & mailers</span></div>
<div><strong>1%</strong><span>of revenue to watershed restoration</span></div>
</div>
<a href="#pricing" class="btn btn--solid">Drink with a clear conscience</a>
</div>
<div class="sustain__art reveal" aria-hidden="true">
<div class="leaf leaf--1"></div>
<div class="leaf leaf--2"></div>
<div class="leaf leaf--3"></div>
<span class="sustain__badge">Direct<br/>Trade</span>
</div>
</div>
</section>
<!-- FAQ -->
<section class="section faq" id="faq">
<div class="wrap faq__inner">
<div class="faq__head reveal">
<span class="eyebrow eyebrow--dark">Good to know</span>
<h2 class="section__title">Questions, answered.</h2>
</div>
<div class="faq__list reveal">
<div class="acc">
<button class="acc__q" aria-expanded="false">
How fresh is the coffee, really?
<span class="acc__icon" aria-hidden="true"></span>
</button>
<div class="acc__a"><p>We roast Monday through Thursday and ship the same or next day. Your bag is stamped with its roast date — most customers receive coffee that was green beans 72 hours earlier.</p></div>
</div>
<div class="acc">
<button class="acc__q" aria-expanded="false">
Whole bean or ground?
<span class="acc__icon" aria-hidden="true"></span>
</button>
<div class="acc__a"><p>Both. Choose whole bean (we recommend it) or pick your brew method at checkout and we'll grind to match — espresso, pour-over, French press or moka pot.</p></div>
</div>
<div class="acc">
<button class="acc__q" aria-expanded="false">
Can I pause or cancel my subscription?
<span class="acc__icon" aria-hidden="true"></span>
</button>
<div class="acc__a"><p>Anytime, from your account in two clicks. Skip a delivery, change your roast, or cancel outright — no emails, no retention maze, no fees.</p></div>
</div>
<div class="acc">
<button class="acc__q" aria-expanded="false">
Where do you ship?
<span class="acc__icon" aria-hidden="true"></span>
</button>
<div class="acc__a"><p>Across the continental US with carbon-neutral carriers. Orders over $35 ship free; subscriptions always ship free.</p></div>
</div>
</div>
</div>
</section>
<!-- FINAL CTA -->
<section class="section finalcta">
<div class="wrap finalcta__inner reveal">
<h2>Your next great cup is one batch away.</h2>
<p>Join 18,000 people who wake up to coffee roasted just for them.</p>
<a href="#pricing" class="btn btn--solid btn--lg" data-cart-add="Driftwood Sampler">Start your subscription</a>
</div>
</section>
</main>
<!-- FOOTER -->
<footer class="footer">
<div class="wrap footer__inner">
<div class="footer__brand">
<span class="brand__name">Driftwood<span class="brand__sub">Roasters</span></span>
<p>Small-batch, direct-trade coffee roasted to order in Asheville, NC.</p>
</div>
<div class="footer__cols">
<div><h4>Shop</h4><a href="#roast">Single origins</a><a href="#pricing">Subscriptions</a><a href="#pricing">Gift sets</a><a href="#flavor">Tasting notes</a></div>
<div><h4>Learn</h4><a href="#brew">Brew guides</a><a href="#sustain">Sourcing</a><a href="#faq">FAQ</a><a href="#top">Our story</a></div>
<div><h4>Stay close</h4><a href="#top">Instagram</a><a href="#top">Newsletter</a><a href="#top">Wholesale</a><a href="#top">Cupping events</a></div>
</div>
</div>
<div class="wrap footer__bottom">
<span>© 2026 Driftwood Roasters. A fictional brand.</span>
<span>Privacy · Terms · Made with care & caffeine</span>
</div>
</footer>
<!-- STICKY CART -->
<aside class="sticky-cart" id="stickyCart" hidden>
<div class="sticky-cart__info">
<span class="sticky-cart__count" id="stickyCount">1 item</span>
<span class="sticky-cart__last" id="stickyLast">Yirgacheffe — Light</span>
</div>
<button class="btn btn--solid" id="checkoutBtn">Checkout · <span id="stickyTotal">$19</span></button>
</aside>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Coffee / Beverage Landing
A complete one-page direct-to-consumer landing for Driftwood Roasters, a fictional small-batch specialty coffee brand. The visual identity leans on a kraft-paper and espresso-brown palette with cream accents, a rugged serif (Bitter) for headlines paired with a clean sans (Work Sans) for body copy, and a mono accent for technical labels — an artisanal, warm, craft mood throughout. The hero opens with an origin-story hook and an animated coffee-bag product mockup complete with drifting steam.
Every section is self-contained and responsive down to ~360px. Highlights include an interactive roast-level selector that recolors a coffee bean and updates body, acidity and sweetness meters as you drag the slider; a flavor-notes grid; a four-step pour-over brewing guide; a subscribe-vs-one-time pricing toggle that swaps prices live and saves 20%; verified-style reviews; a sustainability panel with sourcing stats; and an accordion FAQ. A sticky cart slides up as you add bags, with a toast helper confirming each action.
The interactions are written in dependency-free vanilla JavaScript: sticky-nav state, mobile menu toggle, smooth scrolling with a nav offset, IntersectionObserver scroll-reveal, the roast slider, the pricing toggle, the FAQ accordion, and the cart/sticky-cart/toast logic. All copy, brands and testimonials are realistic but fictional, with no placeholder text.
Illustrative UI only — fictional brand, not a real product.